diff --git a/.gitignore b/.gitignore index 59e4e44..7eb232d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ out/ cache/ lcov.info test/reference/RPC.sol + +# environment +.env diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..db77d70 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "lib/uniswap/v3-periphery"] + path = lib/uniswap/v3-periphery + url = https://github.com/Uniswap/v3-periphery +[submodule "lib/uniswap/v3-core"] + path = lib/uniswap/v3-core + url = https://github.com/Uniswap/v3-core +[submodule "lib/ds-test"] + path = lib/ds-test + url = https://github.com/dapphub/ds-test.git +[submodule "lib/canonical-weth"] + path = lib/canonical-weth + url = https://github.com/gnosis/canonical-weth.git +[submodule "lib/chainlink"] + path = lib/chainlink + url = https://github.com/smartcontractkit/chainlink diff --git a/deploy.txt b/deploy.txt new file mode 100644 index 0000000..974c77d --- /dev/null +++ b/deploy.txt @@ -0,0 +1 @@ +forge create --rpc-url %INFURA_GOERLI% --constructor-args 100 0xE592427A0AEce92De3Edee1F18E0157C05861564 --private-key %PRIVATE_KEY% --etherscan-api-key %ETHERSCAN_API_KEY% --verify src/KyotoPay.sol:KyotoPay diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..fa8b011 --- /dev/null +++ b/foundry.toml @@ -0,0 +1,15 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +gas_reports = ["KyotoPay", "Deployer"] +remappings = ["ds-test/=lib/ds-test/src", "@openzeppelin/=lib/openzeppelin-contracts/", "@uniswap=lib/uniswap", "@chainlink=lib/chainlink"] + +[rpc_endpoints] +mainnet = "${MAINNET_RPC_URL}" +goerli = "${GOERLI_RPC_URL}" +polygon = "${POLYGON_RPC_URL}" + +[etherscan] +mainnet = { key = "${ETHERSCAN_MAINNET_KEY}" } +goerli = { key = "${ETHERSCAN_MAINNET_KEY}", chain = "goerli" } diff --git a/lib/canonical-weth/.eslintrc.js b/lib/canonical-weth/.eslintrc.js new file mode 100644 index 0000000..cf6be88 --- /dev/null +++ b/lib/canonical-weth/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + env: { es6: true, node: true }, + extends: ["plugin:prettier/recommended"] +}; diff --git a/lib/canonical-weth/.gitignore b/lib/canonical-weth/.gitignore new file mode 100644 index 0000000..00cbbdf --- /dev/null +++ b/lib/canonical-weth/.gitignore @@ -0,0 +1,59 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + diff --git a/lib/canonical-weth/.prettierignore b/lib/canonical-weth/.prettierignore new file mode 100644 index 0000000..0037b81 --- /dev/null +++ b/lib/canonical-weth/.prettierignore @@ -0,0 +1,3 @@ +package.json +package-lock.json +build/ diff --git a/lib/canonical-weth/.travis.yml b/lib/canonical-weth/.travis.yml new file mode 100644 index 0000000..e32a325 --- /dev/null +++ b/lib/canonical-weth/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "node" + - "8" diff --git a/lib/canonical-weth/LICENSE b/lib/canonical-weth/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/canonical-weth/LICENSE @@ -0,0 +1,674 @@ + 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/lib/canonical-weth/README.md b/lib/canonical-weth/README.md new file mode 100644 index 0000000..2f6fb6d --- /dev/null +++ b/lib/canonical-weth/README.md @@ -0,0 +1,25 @@ +# Canonical W-ETH + +Canonical [W-ETH](https://weth.io/) package (see https://blog.0xproject.com/canonical-weth-a9aa7d0279dd) + +## Usage + +```sh +npm install --save truffle-contract canonical-weth +``` + +and + +```js +const contract = require('truffle-contract'); +const wethArtifact = require('canonical-weth'); + +const weth = contract(wethArtifact); +``` + +## Deployed contract addresses + +- Mainnet: [0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) +- Kovan: [0xd0a1e359811322d97991e03f863a0c30c2cf029c](https://kovan.etherscan.io/address/0xd0a1e359811322d97991e03f863a0c30c2cf029c) +- Ropsten: [0xc778417e063141139fce010982780140aa0cd5ab](https://ropsten.etherscan.io/address/0xc778417e063141139fce010982780140aa0cd5ab) +- Rinkeby: [0xc778417e063141139fce010982780140aa0cd5ab](https://rinkeby.etherscan.io/address/0xc778417e063141139fce010982780140aa0cd5ab) diff --git a/lib/canonical-weth/build/contracts/Migrations.json b/lib/canonical-weth/build/contracts/Migrations.json new file mode 100644 index 0000000..10427ff --- /dev/null +++ b/lib/canonical-weth/build/contracts/Migrations.json @@ -0,0 +1 @@ +{"contractName":"Migrations","abi":[{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastCompletedMigration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":false,"inputs":[{"name":"completed","type":"uint256"}],"name":"setCompleted","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAddress","type":"address"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}],"bytecode":"0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610314806100606000396000f3fe608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f010146100675780638da5cb5b146100b8578063fbdbad3c1461010f578063fdacd5761461013a575b600080fd5b34801561007357600080fd5b506100b66004803603602081101561008a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610175565b005b3480156100c457600080fd5b506100cd61025d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561011b57600080fd5b50610124610282565b6040518082815260200191505060405180910390f35b34801561014657600080fd5b506101736004803603602081101561015d57600080fd5b8101908080359060200190929190505050610288565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561025a5760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561024057600080fd5b505af1158015610254573d6000803e3d6000fd5b50505050505b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102e557806001819055505b5056fea165627a7a72305820acff9e0ed01ece0c2a77537d3df6e8a2cf6a926c55e915447e3fd41110371c200029","deployedBytecode":"0x608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f010146100675780638da5cb5b146100b8578063fbdbad3c1461010f578063fdacd5761461013a575b600080fd5b34801561007357600080fd5b506100b66004803603602081101561008a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610175565b005b3480156100c457600080fd5b506100cd61025d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561011b57600080fd5b50610124610282565b6040518082815260200191505060405180910390f35b34801561014657600080fd5b506101736004803603602081101561015d57600080fd5b8101908080359060200190929190505050610288565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561025a5760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561024057600080fd5b505af1158015610254573d6000803e3d6000fd5b50505050505b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102e557806001819055505b5056fea165627a7a72305820acff9e0ed01ece0c2a77537d3df6e8a2cf6a926c55e915447e3fd41110371c200029","sourceMap":"32:512:0:-;;;125:56;8:9:-1;5:2;;;30:1;27;20:12;5:2;125:56:0;164:10;156:5;;:18;;;;;;;;;;;;;;;;;;32:512;;;;;;","deployedSourceMap":"32:512:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;371:171;;8:9:-1;5:2;;;30:1;27;20:12;5:2;371:171:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;371:171:0;;;;;;;;;;;;;;;;;;;;;;58:20;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58:20:0;;;;;;;;;;;;;;;;;;;;;;;;;;;84:34;;8:9:-1;5:2;;;30:1;27;20:12;5:2;84:34:0;;;;;;;;;;;;;;;;;;;;;;;258:107;;8:9:-1;5:2;;;30:1;27;20:12;5:2;258:107:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;258:107:0;;;;;;;;;;;;;;;;;;;;371:171;237:5;;;;;;;;;;;223:19;;:10;:19;;;219:26;;;436:19;469:10;436:44;;490:8;:21;;;512:22;;490:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;490:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;490:45:0;;;;244:1;219:26;371:171;:::o;58:20::-;;;;;;;;;;;;;:::o;84:34::-;;;;:::o;258:107::-;237:5;;;;;;;;;;;223:19;;:10;:19;;;219:26;;;349:9;324:22;:34;;;;219:26;258:107;:::o","source":"pragma solidity >=0.4.22 <0.6;\n\ncontract Migrations {\n address public owner;\n uint public lastCompletedMigration;\n\n constructor() public {\n owner = msg.sender;\n }\n\n modifier restricted() {\n if (msg.sender == owner) _;\n }\n\n function setCompleted(uint completed) public restricted {\n lastCompletedMigration = completed;\n }\n\n function upgrade(address newAddress) public restricted {\n Migrations upgraded = Migrations(newAddress);\n upgraded.setCompleted(lastCompletedMigration);\n }\n}\n","compiler":{"name":"solc","version":"0.5.0+commit.1d4f565a.Emscripten.clang"},"networks":{},"schemaVersion":"3.0.2","updatedAt":"2019-02-28T18:32:54.004Z","devdoc":{"methods":{}},"userdoc":{"methods":{}}} \ No newline at end of file diff --git a/lib/canonical-weth/build/contracts/WETH9.json b/lib/canonical-weth/build/contracts/WETH9.json new file mode 100644 index 0000000..6b11b4d --- /dev/null +++ b/lib/canonical-weth/build/contracts/WETH9.json @@ -0,0 +1,325 @@ +{ + "contractName": "WETH9", + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": true, + "name": "dst", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "dst", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "wad", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040526040805190810160405280600d81526020017f57726170706564204574686572000000000000000000000000000000000000008152506000908051906020019061004f9291906100ca565b506040805190810160405280600481526020017f57455448000000000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100ca565b506012600260006101000a81548160ff021916908360ff1602179055503480156100c457600080fd5b5061016f565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010b57805160ff1916838001178555610139565b82800160010185558215610139579182015b8281111561013857825182559160200191906001019061011d565b5b509050610146919061014a565b5090565b61016c91905b80821115610168576000816000905550600101610150565b5090565b90565b610cd88061017e6000396000f3fe6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014957806318160ddd146101bc57806323b872dd146101e75780632e1a7d4d1461027a578063313ce567146102b557806370a08231146102e657806395d89b411461034b578063a9059cbb146103db578063d0e30db01461044e578063dd62ed3e14610458575b6100b76104dd565b005b3480156100c557600080fd5b506100ce61057a565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010e5780820151818401526020810190506100f3565b50505050905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015557600080fd5b506101a26004803603604081101561016c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610618565b604051808215151515815260200191505060405180910390f35b3480156101c857600080fd5b506101d161070a565b6040518082815260200191505060405180910390f35b3480156101f357600080fd5b506102606004803603606081101561020a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610729565b604051808215151515815260200191505060405180910390f35b34801561028657600080fd5b506102b36004803603602081101561029d57600080fd5b8101908080359060200190929190505050610a76565b005b3480156102c157600080fd5b506102ca610ba9565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102f257600080fd5b506103356004803603602081101561030957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bbc565b6040518082815260200191505060405180910390f35b34801561035757600080fd5b50610360610bd4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103a0578082015181840152602081019050610385565b50505050905090810190601f1680156103cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156103e757600080fd5b50610434600480360360408110156103fe57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610c72565b604051808215151515815260200191505060405180910390f35b6104566104dd565b005b34801561046457600080fd5b506104c76004803603604081101561047b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c87565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106105780601f106105e557610100808354040283529160200191610610565b820191906000526020600020905b8154815290600101906020018083116105f357829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561077957600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415801561085157507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b1561096c5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156108e157600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ac457600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015610b57573d6000803e3d6000fd5b503373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c6a5780601f10610c3f57610100808354040283529160200191610c6a565b820191906000526020600020905b815481529060010190602001808311610c4d57829003601f168201915b505050505081565b6000610c7f338484610729565b905092915050565b600460205281600052604060002060205280600052604060002060009150915050548156fea165627a7a7230582089f5a509ec49def9e0fd69996f7ea7cb42adb14f134f71ea034b8ce39df0a1e00029", + "deployedBytecode": "0x6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014957806318160ddd146101bc57806323b872dd146101e75780632e1a7d4d1461027a578063313ce567146102b557806370a08231146102e657806395d89b411461034b578063a9059cbb146103db578063d0e30db01461044e578063dd62ed3e14610458575b6100b76104dd565b005b3480156100c557600080fd5b506100ce61057a565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010e5780820151818401526020810190506100f3565b50505050905090810190601f16801561013b5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015557600080fd5b506101a26004803603604081101561016c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610618565b604051808215151515815260200191505060405180910390f35b3480156101c857600080fd5b506101d161070a565b6040518082815260200191505060405180910390f35b3480156101f357600080fd5b506102606004803603606081101561020a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610729565b604051808215151515815260200191505060405180910390f35b34801561028657600080fd5b506102b36004803603602081101561029d57600080fd5b8101908080359060200190929190505050610a76565b005b3480156102c157600080fd5b506102ca610ba9565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102f257600080fd5b506103356004803603602081101561030957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bbc565b6040518082815260200191505060405180910390f35b34801561035757600080fd5b50610360610bd4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103a0578082015181840152602081019050610385565b50505050905090810190601f1680156103cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156103e757600080fd5b50610434600480360360408110156103fe57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610c72565b604051808215151515815260200191505060405180910390f35b6104566104dd565b005b34801561046457600080fd5b506104c76004803603604081101561047b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c87565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106105780601f106105e557610100808354040283529160200191610610565b820191906000526020600020905b8154815290600101906020018083116105f357829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561077957600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415801561085157507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b1561096c5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156108e157600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ac457600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015610b57573d6000803e3d6000fd5b503373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c6a5780601f10610c3f57610100808354040283529160200191610c6a565b820191906000526020600020905b815481529060010190602001808311610c4d57829003601f168201915b505050505081565b6000610c7f338484610729565b905092915050565b600460205281600052604060002060205280600052604060002060009150915050548156fea165627a7a7230582089f5a509ec49def9e0fd69996f7ea7cb42adb14f134f71ea034b8ce39df0a1e00029", + "sourceMap": "718:1809:1:-;;;739:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;785:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;847:2;822:27;;;;;;;;;;;;;;;;;;;;718:1809;8:9:-1;5:2;;;30:1;27;20:12;5:2;718:1809:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;", + "deployedSourceMap": "718:1809:1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1289:9;:7;:9::i;:::-;718:1809;739:40;;8:9:-1;5:2;;;30:1;27;20:12;5:2;739:40:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;739:40:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1755:177;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1755:177:1;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;1755:177:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1654:95;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1654:95:1;;;;;;;;;;;;;;;;;;;;;;;2065:460;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2065:460:1;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;2065:460:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1445:203;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1445:203:1;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;1445:203:1;;;;;;;;;;;;;;;;;;;;822:27;;8:9:-1;5:2;;;30:1;27;20:12;5:2;822:27:1;;;;;;;;;;;;;;;;;;;;;;;;;;;1108:65;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1108:65:1;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;1108:65:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;785:31;;8:9:-1;5:2;;;30:1;27;20:12;5:2;785:31:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;99:1;94:3;90:11;84:18;80:1;75:3;71:11;64:39;52:2;49:1;45:10;40:15;;8:100;;;12:14;785:31:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1938:121;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1938:121:1;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;1938:121:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1310:130;;;;;;1179:65;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1179:65:1;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;1179:65:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1310:130;1379:9;1354;:21;1364:10;1354:21;;;;;;;;;;;;;;;;:34;;;;;;;;;;;1411:10;1403:30;;;1423:9;1403:30;;;;;;;;;;;;;;;;;;1310:130::o;739:40::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1755:177::-;1811:4;1856:3;1827:9;:21;1837:10;1827:21;;;;;;;;;;;;;;;:26;1849:3;1827:26;;;;;;;;;;;;;;;:32;;;;1895:3;1874:30;;1883:10;1874:30;;;1900:3;1874:30;;;;;;;;;;;;;;;;;;1921:4;1914:11;;1755:177;;;;:::o;1654:95::-;1698:4;1729;1721:21;;;1714:28;;1654:95;:::o;2065:460::-;2155:4;2201:3;2183:9;:14;2193:3;2183:14;;;;;;;;;;;;;;;;:21;;2175:30;;;;;;;;2227:10;2220:17;;:3;:17;;;;:59;;;;;2276:2;2241:9;:14;2251:3;2241:14;;;;;;;;;;;;;;;:26;2256:10;2241:26;;;;;;;;;;;;;;;;:38;;2220:59;2216:179;;;2333:3;2303:9;:14;2313:3;2303:14;;;;;;;;;;;;;;;:26;2318:10;2303:26;;;;;;;;;;;;;;;;:33;;2295:42;;;;;;;;2381:3;2351:9;:14;2361:3;2351:14;;;;;;;;;;;;;;;:26;2366:10;2351:26;;;;;;;;;;;;;;;;:33;;;;;;;;;;;2216:179;2423:3;2405:9;:14;2415:3;2405:14;;;;;;;;;;;;;;;;:21;;;;;;;;;;;2454:3;2436:9;:14;2446:3;2436:14;;;;;;;;;;;;;;;;:21;;;;;;;;;;;2487:3;2473:23;;2482:3;2473:23;;;2492:3;2473:23;;;;;;;;;;;;;;;;;;2514:4;2507:11;;2065:460;;;;;:::o;1445:203::-;1523:3;1498:9;:21;1508:10;1498:21;;;;;;;;;;;;;;;;:28;;1490:37;;;;;;;;1562:3;1537:9;:21;1547:10;1537:21;;;;;;;;;;;;;;;;:28;;;;;;;;;;;1575:10;:19;;:24;1595:3;1575:24;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1575:24:1;1625:10;1614:27;;;1637:3;1614:27;;;;;;;;;;;;;;;;;;1445:203;:::o;822:27::-;;;;;;;;;;;;;:::o;1108:65::-;;;;;;;;;;;;;;;;;:::o;785:31::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1938:121::-;1995:4;2018:34;2031:10;2043:3;2048;2018:12;:34::i;:::-;2011:41;;1938:121;;;;:::o;1179:65::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o", + "source": "// Copyright (C) 2015, 2016, 2017 Dapphub\n\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n\n// You should have received a copy of the GNU General Public License\n// along with this program. If not, see .\n\npragma solidity >=0.4.22 <0.6;\n\ncontract WETH9 {\n string public name = \"Wrapped Ether\";\n string public symbol = \"WETH\";\n uint8 public decimals = 18;\n\n event Approval(address indexed src, address indexed guy, uint wad);\n event Transfer(address indexed src, address indexed dst, uint wad);\n event Deposit(address indexed dst, uint wad);\n event Withdrawal(address indexed src, uint wad);\n\n mapping (address => uint) public balanceOf;\n mapping (address => mapping (address => uint)) public allowance;\n\n function() external payable {\n deposit();\n }\n function deposit() public payable {\n balanceOf[msg.sender] += msg.value;\n emit Deposit(msg.sender, msg.value);\n }\n function withdraw(uint wad) public {\n require(balanceOf[msg.sender] >= wad);\n balanceOf[msg.sender] -= wad;\n msg.sender.transfer(wad);\n emit Withdrawal(msg.sender, wad);\n }\n\n function totalSupply() public view returns (uint) {\n return address(this).balance;\n }\n\n function approve(address guy, uint wad) public returns (bool) {\n allowance[msg.sender][guy] = wad;\n emit Approval(msg.sender, guy, wad);\n return true;\n }\n\n function transfer(address dst, uint wad) public returns (bool) {\n return transferFrom(msg.sender, dst, wad);\n }\n\n function transferFrom(address src, address dst, uint wad)\n public\n returns (bool)\n {\n require(balanceOf[src] >= wad);\n\n if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {\n require(allowance[src][msg.sender] >= wad);\n allowance[src][msg.sender] -= wad;\n }\n\n balanceOf[src] -= wad;\n balanceOf[dst] += wad;\n\n emit Transfer(src, dst, wad);\n\n return true;\n }\n}\n\n\n/*\n GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. \n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n Preamble\n\n The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works. By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users. We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors. You can apply it to\nyour programs, too.\n\n When we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights. Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received. You must make sure that they, too, receive\nor can get the source code. And you must show them these terms so they\nknow their rights.\n\n Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software. For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so. This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software. The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable. Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts. If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary. To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n The precise terms and conditions for copying, distribution and\nmodification follow.\n\n TERMS AND CONDITIONS\n\n 0. Definitions.\n\n \"This License\" refers to version 3 of the GNU General Public License.\n\n \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n \"The Program\" refers to any copyrightable work licensed under this\nLicense. Each licensee is addressed as \"you\". \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy. The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies. Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License. If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n 1. Source Code.\n\n The \"source code\" for a work means the preferred form of the work\nfor making modifications to it. \"Object code\" means any non-source\nform of a work.\n\n A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form. A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities. However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work. For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n The Corresponding Source for a work in source code form is that\nsame work.\n\n 2. Basic Permissions.\n\n All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met. This License explicitly affirms your unlimited\npermission to run the unmodified Program. The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work. This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force. You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright. Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n Conveying under any other circumstances is permitted solely under\nthe conditions stated below. Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n 4. Conveying Verbatim Copies.\n\n You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n 5. Conveying Modified Source Versions.\n\n You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n a) The work must carry prominent notices stating that you modified\n it, and giving a relevant date.\n\n b) The work must carry prominent notices stating that it is\n released under this License and any conditions added under section\n 7. This requirement modifies the requirement in section 4 to\n \"keep intact all notices\".\n\n c) You must license the entire work, as a whole, under this\n License to anyone who comes into possession of a copy. This\n License will therefore apply, along with any applicable section 7\n additional terms, to the whole of the work, and all its parts,\n regardless of how they are packaged. This License gives no\n permission to license the work in any other way, but it does not\n invalidate such permission if you have separately received it.\n\n d) If the work has interactive user interfaces, each must display\n Appropriate Legal Notices; however, if the Program has interactive\n interfaces that do not display Appropriate Legal Notices, your\n work need not make them do so.\n\n A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit. Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n 6. Conveying Non-Source Forms.\n\n You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n a) Convey the object code in, or embodied in, a physical product\n (including a physical distribution medium), accompanied by the\n Corresponding Source fixed on a durable physical medium\n customarily used for software interchange.\n\n b) Convey the object code in, or embodied in, a physical product\n (including a physical distribution medium), accompanied by a\n written offer, valid for at least three years and valid for as\n long as you offer spare parts or customer support for that product\n model, to give anyone who possesses the object code either (1) a\n copy of the Corresponding Source for all the software in the\n product that is covered by this License, on a durable physical\n medium customarily used for software interchange, for a price no\n more than your reasonable cost of physically performing this\n conveying of source, or (2) access to copy the\n Corresponding Source from a network server at no charge.\n\n c) Convey individual copies of the object code with a copy of the\n written offer to provide the Corresponding Source. This\n alternative is allowed only occasionally and noncommercially, and\n only if you received the object code with such an offer, in accord\n with subsection 6b.\n\n d) Convey the object code by offering access from a designated\n place (gratis or for a charge), and offer equivalent access to the\n Corresponding Source in the same way through the same place at no\n further charge. You need not require recipients to copy the\n Corresponding Source along with the object code. If the place to\n copy the object code is a network server, the Corresponding Source\n may be on a different server (operated by you or a third party)\n that supports equivalent copying facilities, provided you maintain\n clear directions next to the object code saying where to find the\n Corresponding Source. Regardless of what server hosts the\n Corresponding Source, you remain obligated to ensure that it is\n available for as long as needed to satisfy these requirements.\n\n e) Convey the object code using peer-to-peer transmission, provided\n you inform other peers where the object code and Corresponding\n Source of the work are being offered to the general public at no\n charge under subsection 6d.\n\n A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling. In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage. For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product. A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source. The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information. But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed. Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n 7. Additional Terms.\n\n \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law. If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit. (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.) You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n a) Disclaiming warranty or limiting liability differently from the\n terms of sections 15 and 16 of this License; or\n\n b) Requiring preservation of specified reasonable legal notices or\n author attributions in that material or in the Appropriate Legal\n Notices displayed by works containing it; or\n\n c) Prohibiting misrepresentation of the origin of that material, or\n requiring that modified versions of such material be marked in\n reasonable ways as different from the original version; or\n\n d) Limiting the use for publicity purposes of names of licensors or\n authors of the material; or\n\n e) Declining to grant rights under trademark law for use of some\n trade names, trademarks, or service marks; or\n\n f) Requiring indemnification of licensors and authors of that\n material by anyone who conveys the material (or modified versions of\n it) with contractual assumptions of liability to the recipient, for\n any liability that these contractual assumptions directly impose on\n those licensors and authors.\n\n All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10. If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term. If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n 8. Termination.\n\n You may not propagate or modify a covered work except as expressly\nprovided under this License. Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License. If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n 9. Acceptance Not Required for Having Copies.\n\n You are not required to accept this License in order to receive or\nrun a copy of the Program. Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance. However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work. These actions infringe copyright if you do\nnot accept this License. Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n 10. Automatic Licensing of Downstream Recipients.\n\n Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License. You are not responsible\nfor enforcing compliance by third parties with this License.\n\n An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations. If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License. For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n 11. Patents.\n\n A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based. The\nwork thus licensed is called the contributor's \"contributor version\".\n\n A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version. For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement). To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients. \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License. You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n 12. No Surrender of Others' Freedom.\n\n If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License. If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all. For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n 13. Use with the GNU Affero General Public License.\n\n Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work. The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n 14. Revised Versions of this License.\n\n The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time. Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n Each version is given a distinguishing version number. If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation. If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n Later license versions may give you additional or different\npermissions. However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n 15. Disclaimer of Warranty.\n\n THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n 16. Limitation of Liability.\n\n IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n 17. Interpretation of Sections 15 and 16.\n\n If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n END OF TERMS AND CONDITIONS\n\n How to Apply These Terms to Your New Programs\n\n If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n To do so, attach the following notices to the program. It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n \n Copyright (C) \n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 3 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program. If not, see .\n\nAlso add information on how to contact you by electronic and paper mail.\n\n If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n Copyright (C) \n This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n This is free software, and you are welcome to redistribute it\n under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License. Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n.\n\n The GNU General Public License does not permit incorporating your program\ninto proprietary programs. If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library. If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License. But first, please read\n.\n\n*/", + "compiler": { + "name": "solc", + "version": "0.5.0+commit.1d4f565a.Emscripten.clang" + }, + "networks": { + "1": { + "events": {}, + "links": {}, + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "transactionHash": "0xb95343413e459a0f97461812111254163ae53467855c0d73e0f1e7c5b8442fa3" + }, + "3": { + "events": {}, + "links": {}, + "address": "0xc778417e063141139fce010982780140aa0cd5ab", + "transactionHash": "0x19ae7fb1bd96c6f623741f76573a3e97d8a863358dafb1d773a8f9ad98b424b4" + }, + "4": { + "events": {}, + "links": {}, + "address": "0xc778417e063141139fce010982780140aa0cd5ab", + "transactionHash": "0x7bc8e85f99556aa23a41dd3c107e92ec76f057e4cea39f376ffb1b15d514b11f" + }, + "42": { + "events": {}, + "links": {}, + "address": "0xd0a1e359811322d97991e03f863a0c30c2cf029c", + "transactionHash": "0x0e8d602b350e2a896134d79b48b8a59488a0a70933d46c5736c47b468877be35" + } + }, + "schemaVersion": "3.0.2", + "updatedAt": "2019-02-28T18:32:54.006Z", + "devdoc": { + "methods": {} + }, + "userdoc": { + "methods": {} + } +} \ No newline at end of file diff --git a/lib/canonical-weth/contracts/Migrations.sol b/lib/canonical-weth/contracts/Migrations.sol new file mode 100644 index 0000000..51c0b63 --- /dev/null +++ b/lib/canonical-weth/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.22 <0.6; + +contract Migrations { + address public owner; + uint public lastCompletedMigration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + lastCompletedMigration = completed; + } + + function upgrade(address newAddress) public restricted { + Migrations upgraded = Migrations(newAddress); + upgraded.setCompleted(lastCompletedMigration); + } +} diff --git a/lib/canonical-weth/contracts/WETH9.sol b/lib/canonical-weth/contracts/WETH9.sol new file mode 100644 index 0000000..3660ff2 --- /dev/null +++ b/lib/canonical-weth/contracts/WETH9.sol @@ -0,0 +1,756 @@ +// Copyright (C) 2015, 2016, 2017 Dapphub + +// 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 . + +pragma solidity >=0.4.22 <0.6; + +contract WETH9 { + string public name = "Wrapped Ether"; + string public symbol = "WETH"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Deposit(address indexed dst, uint wad); + event Withdrawal(address indexed src, uint wad); + + mapping (address => uint) public balanceOf; + mapping (address => mapping (address => uint)) public allowance; + + function() external payable { + deposit(); + } + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + emit Deposit(msg.sender, msg.value); + } + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + emit Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return address(this).balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + emit Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + emit Transfer(src, dst, wad); + + return true; + } +} + + +/* + 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 +. + +*/ \ No newline at end of file diff --git a/lib/canonical-weth/index.js b/lib/canonical-weth/index.js new file mode 100644 index 0000000..702c57b --- /dev/null +++ b/lib/canonical-weth/index.js @@ -0,0 +1,314 @@ +module.exports = { + contractName: "WETH9", + abi: [ + { + constant: true, + inputs: [], + name: "name", + outputs: [ + { + name: "", + type: "string" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [], + name: "decimals", + outputs: [ + { + name: "", + type: "uint8" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [], + name: "symbol", + outputs: [ + { + name: "", + type: "string" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "", + type: "address" + }, + { + name: "", + type: "address" + } + ], + name: "allowance", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + payable: true, + stateMutability: "payable", + type: "fallback" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: "src", + type: "address" + }, + { + indexed: true, + name: "guy", + type: "address" + }, + { + indexed: false, + name: "wad", + type: "uint256" + } + ], + name: "Approval", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: "src", + type: "address" + }, + { + indexed: true, + name: "dst", + type: "address" + }, + { + indexed: false, + name: "wad", + type: "uint256" + } + ], + name: "Transfer", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: "dst", + type: "address" + }, + { + indexed: false, + name: "wad", + type: "uint256" + } + ], + name: "Deposit", + type: "event" + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + name: "src", + type: "address" + }, + { + indexed: false, + name: "wad", + type: "uint256" + } + ], + name: "Withdrawal", + type: "event" + }, + { + constant: false, + inputs: [], + name: "deposit", + outputs: [], + payable: true, + stateMutability: "payable", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "wad", + type: "uint256" + } + ], + name: "withdraw", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "guy", + type: "address" + }, + { + name: "wad", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "dst", + type: "address" + }, + { + name: "wad", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "src", + type: "address" + }, + { + name: "dst", + type: "address" + }, + { + name: "wad", + type: "uint256" + } + ], + name: "transferFrom", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } + ], + networks: { + "1": { + events: {}, + links: {}, + address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + transactionHash: + "0xb95343413e459a0f97461812111254163ae53467855c0d73e0f1e7c5b8442fa3" + }, + "3": { + events: {}, + links: {}, + address: "0xc778417e063141139fce010982780140aa0cd5ab", + transactionHash: + "0x19ae7fb1bd96c6f623741f76573a3e97d8a863358dafb1d773a8f9ad98b424b4" + }, + "4": { + events: {}, + links: {}, + address: "0xc778417e063141139fce010982780140aa0cd5ab", + transactionHash: + "0x7bc8e85f99556aa23a41dd3c107e92ec76f057e4cea39f376ffb1b15d514b11f" + }, + "42": { + events: {}, + links: {}, + address: "0xd0a1e359811322d97991e03f863a0c30c2cf029c", + transactionHash: + "0x0e8d602b350e2a896134d79b48b8a59488a0a70933d46c5736c47b468877be35" + } + }, + schemaVersion: "2.0.0", + updatedAt: "2018-05-08T22:28:55.958Z" +}; diff --git a/lib/canonical-weth/migrations/1_initial_migration.js b/lib/canonical-weth/migrations/1_initial_migration.js new file mode 100644 index 0000000..eef9e81 --- /dev/null +++ b/lib/canonical-weth/migrations/1_initial_migration.js @@ -0,0 +1,3 @@ +module.exports = function(deployer) { + deployer.deploy(artifacts.require("Migrations")); +}; diff --git a/lib/canonical-weth/migrations/2_deploy_weth9.js b/lib/canonical-weth/migrations/2_deploy_weth9.js new file mode 100644 index 0000000..28da418 --- /dev/null +++ b/lib/canonical-weth/migrations/2_deploy_weth9.js @@ -0,0 +1,3 @@ +module.exports = function(deployer) { + deployer.deploy(artifacts.require("WETH9")); +}; diff --git a/lib/canonical-weth/networks.json b/lib/canonical-weth/networks.json new file mode 100644 index 0000000..66080a0 --- /dev/null +++ b/lib/canonical-weth/networks.json @@ -0,0 +1,32 @@ +{ + "WETH9": { + "1": { + "events": {}, + "links": {}, + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "transactionHash": + "0xb95343413e459a0f97461812111254163ae53467855c0d73e0f1e7c5b8442fa3" + }, + "3": { + "events": {}, + "links": {}, + "address": "0xc778417e063141139fce010982780140aa0cd5ab", + "transactionHash": + "0x19ae7fb1bd96c6f623741f76573a3e97d8a863358dafb1d773a8f9ad98b424b4" + }, + "4": { + "events": {}, + "links": {}, + "address": "0xc778417e063141139fce010982780140aa0cd5ab", + "transactionHash": + "0x7bc8e85f99556aa23a41dd3c107e92ec76f057e4cea39f376ffb1b15d514b11f" + }, + "42": { + "events": {}, + "links": {}, + "address": "0xd0a1e359811322d97991e03f863a0c30c2cf029c", + "transactionHash": + "0x0e8d602b350e2a896134d79b48b8a59488a0a70933d46c5736c47b468877be35" + } + } +} \ No newline at end of file diff --git a/lib/canonical-weth/package-lock.json b/lib/canonical-weth/package-lock.json new file mode 100644 index 0000000..6d19388 --- /dev/null +++ b/lib/canonical-weth/package-lock.json @@ -0,0 +1,5063 @@ +{ + "name": "canonical-weth", + "version": "1.4.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/runtime": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", + "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.12.0" + } + }, + "@gnosis.pm/truffle-nice-tools": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@gnosis.pm/truffle-nice-tools/-/truffle-nice-tools-1.3.0.tgz", + "integrity": "sha512-uJKHKauzehL4PcjvdQJLwZlq9p9ObGdNkumAogxx1UcdCQTL1JUgd1ENa+fmH37SKF/i8SRQTiQMtIayd+o+uw==", + "dev": true, + "requires": { + "abi-decoder": "^1.2.0", + "chalk": "^2.4.1", + "dotenv": "^6.0.0", + "fs-extra": "^7.0.0", + "lodash": "^4.17.10", + "minimist": "^1.2.0", + "ora": "^3.0.0", + "prepend-file": "^1.3.1", + "walk": "^2.3.14", + "web3": "^1.0.0-beta.35" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@types/bn.js": { + "version": "4.11.4", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.4.tgz", + "integrity": "sha512-AO8WW+aRcKWKQAYTfKLzwnpL6U+TfPqS+haRrhCy5ff04Da8WZud3ZgVjspQXaEXJDcTlsjUEVvL39wegDek5w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.12.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.27.tgz", + "integrity": "sha512-e9wgeY6gaY21on3ve0xAjgBVjGDWq/xUteK0ujsE53bUoxycMkqfnkUgMt6ffZtykZ5X12Mg3T7Pw4TRCObDKg==", + "dev": true + }, + "abi-decoder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/abi-decoder/-/abi-decoder-1.2.0.tgz", + "integrity": "sha512-y2OKSEW4gf2838Eavc56vQY9V46zaXkf3Jl1WpTfUBbzAVrXSr4JRZAAWv55Tv9s5WNz1rVgBgz5d2aJIL1QCg==", + "dev": true, + "requires": { + "web3": "^0.18.4" + }, + "dependencies": { + "web3": { + "version": "0.18.4", + "resolved": "https://registry.npmjs.org/web3/-/web3-0.18.4.tgz", + "integrity": "sha1-gewXhBRUkfLqqJVbMcBgSeB8Xn0=", + "dev": true, + "requires": { + "bignumber.js": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "crypto-js": "^3.1.4", + "utf8": "^2.1.1", + "xhr2": "*", + "xmlhttprequest": "*" + } + } + } + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", + "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=", + "dev": true + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha1-ZBqlXft9am8KgUHEucCqULbCTdU=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "from": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "dev": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sha3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.4.tgz", + "integrity": "sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY=", + "dev": true, + "requires": { + "js-sha3": "^0.6.1", + "safe-buffer": "^5.1.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", + "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", + "dev": true + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", + "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", + "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-spinners": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", + "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==", + "dev": true + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "dev": true, + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "dev": true, + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-walk": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", + "dev": true + }, + "dotenv": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", + "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==", + "dev": true + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "dev": true, + "requires": { + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.4", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.0.1", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz", + "integrity": "sha512-ag8YEyBXsm3nmOv1Hz991VtNNDMRa+MNy8cY47Pl4bw6iuzqKbJajXdqUpiw13STdLLrznxgm1hj9NhxeOYq0A==", + "dev": true, + "requires": { + "get-stdin": "^5.0.1" + } + }, + "eslint-plugin-prettier": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz", + "integrity": "sha512-floiaI4F7hRkTrFe8V2ItOK97QYrX75DjmdzmVITZoAP6Cn06oEDPQRsO6MlHEP/u2SxI3xQ52Kpjw6j5WGfeQ==", + "dev": true, + "requires": { + "fast-diff": "^1.1.1", + "jest-docblock": "^21.0.0" + } + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "dev": true, + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true + } + } + }, + "eth-lib": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", + "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", + "dev": true, + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "keccakjs": "^0.2.1", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethers": { + "version": "4.0.26", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.26.tgz", + "integrity": "sha512-3hK4S8eAGhuWZ/feip5z17MswjGgjb4lEPJqWO/O0dNqToYLSHhvu6gGQPs8d9f+XfpEB2EYexfF0qjhWiZjUA==", + "dev": true, + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", + "dev": true + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=", + "dev": true + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=", + "dev": true + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "dev": true, + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", + "dev": true + } + } + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", + "dev": true + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "foreachasync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", + "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.1.tgz", + "integrity": "sha512-SpOZHfz845AH0wJYVuZk2jWDqFmu7Xubsx+ldIpwzy5pDUpu7OJHK7QYNSA2NPlDSKQwM1GFaAkciOWjjW92Sg==", + "dev": true + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "dev": true, + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "globals": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz", + "integrity": "sha512-Dyzmifil8n/TmSqYDEXbm+C8yitzJQqQIlJQLNRMwa+BOUJpRC19pyVeN12JAjt61xonvXjtff+hJruTRXn5HA==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dev": true, + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz", + "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dev": true, + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", + "dev": true, + "requires": { + "safer-buffer": "^2.1.0" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "dev": true, + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", + "dev": true + } + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", + "dev": true + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", + "dev": true + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", + "dev": true + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "jest-docblock": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", + "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", + "dev": true + }, + "js-sha3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.6.1.tgz", + "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", + "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "dev": true, + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "keccakjs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", + "integrity": "sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg==", + "dev": true, + "requires": { + "browserify-sha3": "^0.0.4", + "sha3": "^1.2.2" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", + "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "dev": true + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "dev": true, + "requires": { + "mime-db": "~1.38.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "dev": true, + "requires": { + "mkdirp": "*" + } + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "mock-fs": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.8.0.tgz", + "integrity": "sha512-Gwj4KnJOW15YeTJKO5frFd/WDO5Mc0zxXqL9oHx3+e9rBqW8EVARqQHSaIXznUdljrD6pvbNGW2ZGXKPEfYJfw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "dev": true + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "dev": true, + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", + "dev": true + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true + }, + "oboe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", + "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", + "dev": true, + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "ora": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.1.0.tgz", + "integrity": "sha512-vRBPaNCclUi8pUxRF/G8+5qEQkc6EgzKK1G2ZNJUIGu088Un5qIxFXeDgymvPRM9nmrcUOGzQgS1Vmtz+NtlMw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^1.3.1", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "original-require": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/original-require/-/original-require-1.0.1.tgz", + "integrity": "sha1-DxMEcVhM0zURxew4yNWSE/msXiA=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", + "dev": true + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz", + "integrity": "sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg==", + "dev": true, + "requires": { + "for-each": "^0.3.3", + "string.prototype.trim": "^1.1.2" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-file": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prepend-file/-/prepend-file-1.3.1.tgz", + "integrity": "sha1-g7FuC0rBkB/OiNvZRaIvTMgd9Xk=", + "dev": true, + "requires": { + "tmp": "0.0.31" + }, + "dependencies": { + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.11.1.tgz", + "integrity": "sha512-T/KD65Ot0PB97xTrG8afQ46x3oiVhnfGjGESSI9NWYcG92+OUPZKkwHqGWXH2t9jK1crnQjubECW0FuOth+hxw==", + "dev": true + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", + "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "regenerator-runtime": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", + "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==", + "dev": true + }, + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "*" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "scrypt": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/scrypt/-/scrypt-6.0.3.tgz", + "integrity": "sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0=", + "dev": true, + "requires": { + "nan": "^2.0.8" + } + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==", + "dev": true + }, + "scrypt.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/scrypt.js/-/scrypt.js-0.2.0.tgz", + "integrity": "sha1-r40UZbcemZARC+38WTuUeeA6ito=", + "dev": true, + "requires": { + "scrypt": "^6.0.2", + "scryptsy": "^1.2.1" + } + }, + "scryptsy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz", + "integrity": "sha1-oyJfpLJST4AnAHYeKFW987LZIWM=", + "dev": true, + "requires": { + "pbkdf2": "^3.0.3" + } + }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "dev": true, + "requires": { + "commander": "~2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "dev": true, + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "sha3": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.2.tgz", + "integrity": "sha1-pmxQmN5MJbyIM27ItIF9AFvKe6k=", + "dev": true, + "requires": { + "nan": "2.10.0" + }, + "dependencies": { + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", + "dev": true + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "dev": true, + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "solc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.5.0.tgz", + "integrity": "sha512-mdLHDl9WeYrN+FIKcMc9PlPfnA9DG9ur5QpCDKcv6VC4RINAsTF4EMuXMZMKoQTvZhtLyJIVH/BZ+KU830Z8Xg==", + "dev": true, + "requires": { + "fs-extra": "^0.30.0", + "keccak": "^1.0.2", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "yargs": "^11.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", + "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz", + "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + } + } + } + }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.0", + "function-bind": "^1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "dev": true, + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + }, + "swarm-js": { + "version": "0.1.39", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.39.tgz", + "integrity": "sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "decompress": "^4.0.0", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "truffle": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/truffle/-/truffle-5.0.5.tgz", + "integrity": "sha512-YKAfHvq3C5hGCc+S+14FNDM8iZPeta9McocKSAkYBxDTiRfyT6yeDrHmSNEU7fzNAc7jFSlwhY7Cs5bXUR3RKg==", + "dev": true, + "requires": { + "app-module-path": "^2.2.0", + "mocha": "^4.1.0", + "original-require": "1.0.1", + "solc": "0.5.0" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "unbzip2-stream": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", + "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", + "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "dev": true, + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=", + "dev": true + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true + }, + "utf8": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz", + "integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walk": { + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz", + "integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==", + "dev": true, + "requires": { + "foreachasync": "^3.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "web3": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.0.0-beta.46.tgz", + "integrity": "sha512-a+apA/pXiivrurVEq5Pv9+0GZ5vC6Utu8M9fojW9YUuvory29WFDg+tg+ITBmMvz/dQxInahNS32xJB6HhZbHg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "@types/node": "^10.12.18", + "web3-bzz": "1.0.0-beta.46", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-eth": "1.0.0-beta.46", + "web3-eth-personal": "1.0.0-beta.46", + "web3-net": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-shh": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-bzz": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.0.0-beta.46.tgz", + "integrity": "sha512-l2gnK3wM+dfBfGIbxwz7ZE9mhDMArrENbLmFDMCrNng/PurrXy16GQJ5ziLCt4D2kyO4VWtnGeG2yGiEJSI+Vg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "@types/node": "^10.12.18", + "lodash": "^4.17.11", + "swarm-js": "^0.1.39" + } + }, + "web3-core": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.0.0-beta.46.tgz", + "integrity": "sha512-9Iqp4Szn52ldo1dIJ3uzfKA73TkSvQKJd4lOwxMKK77qniP0C3qpRjTMZumIGsR3iydT0QWWgmxlaca0B4LRYQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "@types/node": "^10.12.18", + "lodash": "^4.17.11", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-core-helpers": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.46.tgz", + "integrity": "sha512-bYUEVxW1LbhlLjwGHfyQ6otc41F91tPQVuarqWypaiFcYXJxQKxg8n5tEg89ZsiAxU23wlQvQMAJ2vg9r0g33A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "lodash": "^4.17.11", + "web3-eth-iban": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-core-method": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.0.0-beta.46.tgz", + "integrity": "sha512-n++LCk2I6UEfbXJFsDPuw8mPn3T6iQLGMTaTBjhybmu1ok4o11xqxuN/Yv7dXWJVTlf2wDiiCIaLdHGQKdjilA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "eventemitter3": "3.1.0", + "lodash": "^4.17.11", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-promievent": "1.0.0-beta.46", + "web3-core-subscriptions": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-core-promievent": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.46.tgz", + "integrity": "sha512-Z0GuazAKpwEJKjE1BpFF74VQ8/hsGCU2vubF5AHrVHIZCaEKIw2mm7uccKyw6QELweOEDpxTM687Gnw2PU6QTw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "eventemitter3": "^3.1.0" + } + }, + "web3-core-subscriptions": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.46.tgz", + "integrity": "sha512-wgz6pMXS0hKdTiPXeWMbGMoDkxKwaHK9KZeSyRGHfzrTThins2PrbduC9yjl+M862zsb6DCk/oQ+AjnUoMnLow==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "eventemitter3": "^3.1.0", + "lodash": "^4.17.11", + "web3-core-helpers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-eth": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.0.0-beta.46.tgz", + "integrity": "sha512-9gUO3SPBAzL4t31iRmNi/7hK8+35YC3lJz6XH0t1dYhybF8rg5zqxMTjsxQFxcilS9N+4hoCVuA6fVpjAKn4Zg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-core-promievent": "1.0.0-beta.46", + "web3-core-subscriptions": "1.0.0-beta.46", + "web3-eth-abi": "1.0.0-beta.46", + "web3-eth-accounts": "1.0.0-beta.46", + "web3-eth-contract": "1.0.0-beta.46", + "web3-eth-ens": "1.0.0-beta.46", + "web3-eth-iban": "1.0.0-beta.46", + "web3-eth-personal": "1.0.0-beta.46", + "web3-net": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-eth-abi": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.46.tgz", + "integrity": "sha512-Ac0JTptqZPpoiF+5sg6gJlNkFZPO77qRd39YxIKoxXaXLP8JPFujpNQUJBMJU6kxT9fMYmR1T453JQj3aMEHeQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "ethers": "^4.0.0", + "lodash": "^4.17.11", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-eth-accounts": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.46.tgz", + "integrity": "sha512-b5XYe3lcm1JZTqkO0amTIVJcqauFGjSDnCFA/mpYnabJgoQv6119fE2fcLPHSavCetLingCL+zrMqk57IvfYRw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "lodash": "^4.17.11", + "scrypt.js": "0.2.0", + "uuid": "3.3.2", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dev": true, + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + } + } + }, + "web3-eth-contract": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.46.tgz", + "integrity": "sha512-hcr5a9/SC8CwpV7ZNXuLQXEpzDcJudS1OlPYMQjnrFVjkjRtuYNb6ldUTaTK9R2WV5bVws9qQEJbvsD+NPnqHw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "lodash": "^4.17.11", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-core-promievent": "1.0.0-beta.46", + "web3-core-subscriptions": "1.0.0-beta.46", + "web3-eth-abi": "1.0.0-beta.46", + "web3-eth-accounts": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-eth-ens": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.0.0-beta.46.tgz", + "integrity": "sha512-7wXZnFW66ZQUD0gMUTdYsnz5VG9yo/PzJmccL73Swza6NHMpeejTNMgUc/GUzyE3449+Mu/37o3hRu3Z4eWi/w==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "eth-ens-namehash": "2.0.8", + "lodash": "^4.17.11", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-core-promievent": "1.0.0-beta.46", + "web3-eth-abi": "1.0.0-beta.46", + "web3-eth-contract": "1.0.0-beta.46", + "web3-net": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-eth-iban": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.46.tgz", + "integrity": "sha512-IM9VbznRaEs58n7mYbJcPviOLMAiiveqZT1jtlcJ/DbrvMkArV0Xf6n2OyFY+ak3ppddKVMIAGISxhky6r/7cg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "bn.js": "4.11.8", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-eth-personal": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.46.tgz", + "integrity": "sha512-NEmuitdSmoeixXfnw23Br8YZGmAw2iH79gUEvYPto0OQ7edc1LtkZP6h0lFPwXRPhz0Z8UDOg+lv31EN4NKBwA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-net": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-net": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.0.0-beta.46.tgz", + "integrity": "sha512-mSEgq5S1SI/M17pS6UEGYBt6v54gaSYw3hvRmx5pu4zoHMz8YmbUSfZh+98uxtcV/TE9v3rCwZur7/0bar9kCA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "lodash": "^4.17.11", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-providers": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-providers/-/web3-providers-1.0.0-beta.46.tgz", + "integrity": "sha512-5h4dFuASthr+EE8xBpAtQ5b5bngjv1ihIBcyEVQEP+bRtvU7qPKA8It2fQLtDl4iSADJDjhhQmvBPKbSlJ9jWg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "@types/node": "^10.12.18", + "eventemitter3": "3.1.0", + "lodash": "^4.17.11", + "oboe": "2.1.4", + "url-parse": "1.4.4", + "websocket": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible", + "xhr2-cookies": "1.1.0" + } + }, + "web3-shh": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.0.0-beta.46.tgz", + "integrity": "sha512-1Qka0DEXbcC07+HAdXM9i848FseaGi/G719aU0XbuEZgjbLYp8/uuKExrRkq9kLly6CIPwq20lrVRzScblAKQw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "web3-core": "1.0.0-beta.46", + "web3-core-helpers": "1.0.0-beta.46", + "web3-core-method": "1.0.0-beta.46", + "web3-core-subscriptions": "1.0.0-beta.46", + "web3-net": "1.0.0-beta.46", + "web3-providers": "1.0.0-beta.46", + "web3-utils": "1.0.0-beta.46" + } + }, + "web3-utils": { + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.0.0-beta.46.tgz", + "integrity": "sha512-mSz+NrAil2fDZkxTXHPntCclZ8DofMjv8Q+BYR0VAyzTzylpYNXAV0WDdxBp/sXgniWRZXZMF7OkQNWqhZ1J9g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "@types/bn.js": "^4.11.4", + "@types/node": "^10.12.18", + "bn.js": "4.11.8", + "eth-lib": "0.2.8", + "ethjs-unit": "^0.1.6", + "lodash": "^4.17.11", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "utf8": "2.1.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dev": true, + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "utf8": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.1.tgz", + "integrity": "sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=", + "dev": true + } + } + }, + "websocket": { + "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", + "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible", + "dev": true, + "requires": { + "debug": "^2.2.0", + "nan": "^2.3.3", + "typedarray-to-buffer": "^3.1.2", + "yaeti": "^0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "dev": true, + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "dev": true, + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", + "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", + "dev": true, + "requires": { + "xhr-request": "^1.0.1" + } + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=", + "dev": true + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "dev": true, + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.1.tgz", + "integrity": "sha512-HgY0xHGmPPakg6kEDufqxZuXVtvPZcipORC8O7S44iEnwsUmP+qnhReHc6d1dyeIZkrPmYFblh45Z2oeDn++fQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + } + }, + "yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/lib/canonical-weth/package.json b/lib/canonical-weth/package.json new file mode 100644 index 0000000..f6616b2 --- /dev/null +++ b/lib/canonical-weth/package.json @@ -0,0 +1,43 @@ +{ + "directories": { + "test": "test" + }, + "scripts": { + "prepack": "npm run build && eslint . && npm run fmtsrc", + "fmtsrc": "eslint . --fix", + "lint": "eslint .", + "build": "truffle compile --all && tnt cB && tnt iN", + "test": "truffle compile --all --solc-version=0.4.22 && truffle compile --all --solc-version=0.5.0" + }, + "devDependencies": { + "@gnosis.pm/truffle-nice-tools": "^1.3.0", + "eslint": "^4.19.1", + "eslint-config-prettier": "^2.9.0", + "eslint-plugin-prettier": "^2.6.0", + "lodash": "^4.17.11", + "prettier": "^1.11.1", + "truffle": "^5.0.5", + "yargs": "^13.2.1" + }, + "name": "canonical-weth", + "description": "Canonical WETH package (see https://blog.0xproject.com/canonical-weth-a9aa7d0279dd)", + "version": "1.4.0", + "main": "index.js", + "dependencies": {}, + "repository": { + "type": "git", + "url": "git+https://github.com/gnosis/canonical-weth.git" + }, + "keywords": [ + "canonical", + "weth", + "ethereum", + "truffle", + "smart-contract" + ], + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/gnosis/canonical-weth/issues" + }, + "homepage": "https://github.com/gnosis/canonical-weth#readme" +} diff --git a/lib/canonical-weth/truffle-config.js b/lib/canonical-weth/truffle-config.js new file mode 100644 index 0000000..edfb435 --- /dev/null +++ b/lib/canonical-weth/truffle-config.js @@ -0,0 +1,31 @@ +const { argv } = require("yargs"); + +module.exports = { + networks: { + mainnet: { + host: "localhost", + port: 8545, + network_id: "1" + }, + ropsten: { + host: "localhost", + port: 8545, + network_id: "3" + }, + kovan: { + host: "localhost", + port: 8545, + network_id: "42" + }, + rinkeby: { + host: "localhost", + port: 8545, + network_id: "4" + } + }, + compilers: { + solc: { + version: argv.solcVersion + } + } +}; diff --git a/lib/ds-test/.gitignore b/lib/ds-test/.gitignore new file mode 100644 index 0000000..63f0b2c --- /dev/null +++ b/lib/ds-test/.gitignore @@ -0,0 +1,3 @@ +/.dapple +/build +/out diff --git a/lib/ds-test/LICENSE b/lib/ds-test/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/ds-test/LICENSE @@ -0,0 +1,674 @@ + 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/lib/ds-test/Makefile b/lib/ds-test/Makefile new file mode 100644 index 0000000..661dac4 --- /dev/null +++ b/lib/ds-test/Makefile @@ -0,0 +1,14 @@ +all:; dapp build + +test: + -dapp --use solc:0.4.23 build + -dapp --use solc:0.4.26 build + -dapp --use solc:0.5.17 build + -dapp --use solc:0.6.12 build + -dapp --use solc:0.7.5 build + +demo: + DAPP_SRC=demo dapp --use solc:0.7.5 build + -hevm dapp-test --verbose 3 + +.PHONY: test demo diff --git a/lib/ds-test/default.nix b/lib/ds-test/default.nix new file mode 100644 index 0000000..cf65419 --- /dev/null +++ b/lib/ds-test/default.nix @@ -0,0 +1,4 @@ +{ solidityPackage, dappsys }: solidityPackage { + name = "ds-test"; + src = ./src; +} diff --git a/lib/ds-test/demo/demo.sol b/lib/ds-test/demo/demo.sol new file mode 100644 index 0000000..f3bb48e --- /dev/null +++ b/lib/ds-test/demo/demo.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.5.0; + +import "../src/test.sol"; + +contract DemoTest is DSTest { + function test_this() public pure { + require(true); + } + function test_logs() public { + emit log("-- log(string)"); + emit log("a string"); + + emit log("-- log_named_uint(string, uint)"); + emit log_named_uint("uint", 512); + + emit log("-- log_named_int(string, int)"); + emit log_named_int("int", -512); + + emit log("-- log_named_address(string, address)"); + emit log_named_address("address", address(this)); + + emit log("-- log_named_bytes32(string, bytes32)"); + emit log_named_bytes32("bytes32", "a string"); + + emit log("-- log_named_bytes(string, bytes)"); + emit log_named_bytes("bytes", hex"cafefe"); + + emit log("-- log_named_string(string, string)"); + emit log_named_string("string", "a string"); + + emit log("-- log_named_decimal_uint(string, uint, uint)"); + emit log_named_decimal_uint("decimal uint", 1.0e18, 18); + + emit log("-- log_named_decimal_int(string, int, uint)"); + emit log_named_decimal_int("decimal int", -1.0e18, 18); + } + event log_old_named_uint(bytes32,uint); + function test_old_logs() public { + emit log_old_named_uint("key", 500); + emit log_named_bytes32("bkey", "val"); + } + function test_trace() public view { + this.echo("string 1", "string 2"); + } + function test_multiline() public { + emit log("a multiline\\nstring"); + emit log("a multiline string"); + emit log_bytes("a string"); + emit log_bytes("a multiline\nstring"); + emit log_bytes("a multiline\\nstring"); + emit logs(hex"0000"); + emit log_named_bytes("0x0000", hex"0000"); + emit logs(hex"ff"); + } + function echo(string memory s1, string memory s2) public pure + returns (string memory, string memory) + { + return (s1, s2); + } + + function prove_this(uint x) public { + emit log_named_uint("sym x", x); + assertGt(x + 1, 0); + } + + function test_logn() public { + assembly { + log0(0x01, 0x02) + log1(0x01, 0x02, 0x03) + log2(0x01, 0x02, 0x03, 0x04) + log3(0x01, 0x02, 0x03, 0x04, 0x05) + } + } + + event MyEvent(uint, uint indexed, uint, uint indexed); + function test_events() public { + emit MyEvent(1, 2, 3, 4); + } + + function test_asserts() public { + string memory err = "this test has failed!"; + emit log("## assertTrue(bool)\n"); + assertTrue(false); + emit log("\n"); + assertTrue(false, err); + + emit log("\n## assertEq(address,address)\n"); + assertEq(address(this), msg.sender); + emit log("\n"); + assertEq(address(this), msg.sender, err); + + emit log("\n## assertEq32(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(uint,uint)\n"); + assertEq(uint(0), 1); + emit log("\n"); + assertEq(uint(0), 1, err); + + emit log("\n## assertEq(int,int)\n"); + assertEq(-1, -2); + emit log("\n"); + assertEq(-1, -2, err); + + emit log("\n## assertEqDecimal(int,int,uint)\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertEqDecimal(uint,uint,uint)\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGt(uint,uint)\n"); + assertGt(uint(0), 0); + emit log("\n"); + assertGt(uint(0), 0, err); + + emit log("\n## assertGt(int,int)\n"); + assertGt(-1, -1); + emit log("\n"); + assertGt(-1, -1, err); + + emit log("\n## assertGtDecimal(int,int,uint)\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGtDecimal(uint,uint,uint)\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGe(uint,uint)\n"); + assertGe(uint(0), 1); + emit log("\n"); + assertGe(uint(0), 1, err); + + emit log("\n## assertGe(int,int)\n"); + assertGe(-1, 0); + emit log("\n"); + assertGe(-1, 0, err); + + emit log("\n## assertGeDecimal(int,int,uint)\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGeDecimal(uint,uint,uint)\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertLt(uint,uint)\n"); + assertLt(uint(0), 0); + emit log("\n"); + assertLt(uint(0), 0, err); + + emit log("\n## assertLt(int,int)\n"); + assertLt(-1, -1); + emit log("\n"); + assertLt(-1, -1, err); + + emit log("\n## assertLtDecimal(int,int,uint)\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLtDecimal(uint,uint,uint)\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertLe(uint,uint)\n"); + assertLe(uint(1), 0); + emit log("\n"); + assertLe(uint(1), 0, err); + + emit log("\n## assertLe(int,int)\n"); + assertLe(0, -1); + emit log("\n"); + assertLe(0, -1, err); + + emit log("\n## assertLeDecimal(int,int,uint)\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLeDecimal(uint,uint,uint)\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertEq(string,string)\n"); + string memory s1 = "string 1"; + string memory s2 = "string 2"; + assertEq(s1, s2); + emit log("\n"); + assertEq(s1, s2, err); + + emit log("\n## assertEq0(bytes,bytes)\n"); + assertEq0(hex"abcdef01", hex"abcdef02"); + emit log("\n"); + assertEq0(hex"abcdef01", hex"abcdef02", err); + } +} + +contract DemoTestWithSetUp { + function setUp() public { + } + function test_pass() public pure { + } +} diff --git a/lib/ds-test/package.json b/lib/ds-test/package.json new file mode 100644 index 0000000..4802ada --- /dev/null +++ b/lib/ds-test/package.json @@ -0,0 +1,15 @@ +{ + "name": "ds-test", + "version": "1.0.0", + "description": "Assertions, equality checks and other test helpers ", + "bugs": "https://github.com/dapphub/ds-test/issues", + "license": "GPL-3.0", + "author": "Contributors to ds-test", + "files": [ + "src/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/dapphub/ds-test.git" + } +} diff --git a/lib/ds-test/src/test.sol b/lib/ds-test/src/test.sol new file mode 100644 index 0000000..515a3bd --- /dev/null +++ b/lib/ds-test/src/test.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// 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 . + +pragma solidity >=0.5.0; + +contract DSTest { + event log (string); + event logs (bytes); + + event log_address (address); + event log_bytes32 (bytes32); + event log_int (int); + event log_uint (uint); + event log_bytes (bytes); + event log_string (string); + + event log_named_address (string key, address val); + event log_named_bytes32 (string key, bytes32 val); + event log_named_decimal_int (string key, int val, uint decimals); + event log_named_decimal_uint (string key, uint val, uint decimals); + event log_named_int (string key, int val); + event log_named_uint (string key, uint val); + event log_named_bytes (string key, bytes val); + event log_named_string (string key, string val); + + bool public IS_TEST = true; + bool private _failed; + + address constant HEVM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + modifier mayRevert() { _; } + modifier testopts(string memory) { _; } + + function failed() public returns (bool) { + if (_failed) { + return _failed; + } else { + bool globalFailed = false; + if (hasHEVMContext()) { + (, bytes memory retdata) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("load(address,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed")) + ) + ); + globalFailed = abi.decode(retdata, (bool)); + } + return globalFailed; + } + } + + function fail() internal { + if (hasHEVMContext()) { + (bool status, ) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("store(address,bytes32,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) + ) + ); + status; // Silence compiler warnings + } + _failed = true; + } + + function hasHEVMContext() internal view returns (bool) { + uint256 hevmCodeSize = 0; + assembly { + hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) + } + return hevmCodeSize > 0; + } + + modifier logs_gas() { + uint startGas = gasleft(); + _; + uint endGas = gasleft(); + emit log_named_uint("gas", startGas - endGas); + } + + function assertTrue(bool condition) internal { + if (!condition) { + emit log("Error: Assertion Failed"); + fail(); + } + } + + function assertTrue(bool condition, string memory err) internal { + if (!condition) { + emit log_named_string("Error", err); + assertTrue(condition); + } + } + + function assertEq(address a, address b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [address]"); + emit log_named_address(" Expected", b); + emit log_named_address(" Actual", a); + fail(); + } + } + function assertEq(address a, address b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes32 a, bytes32 b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [bytes32]"); + emit log_named_bytes32(" Expected", b); + emit log_named_bytes32(" Actual", a); + fail(); + } + } + function assertEq(bytes32 a, bytes32 b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + function assertEq32(bytes32 a, bytes32 b) internal { + assertEq(a, b); + } + function assertEq32(bytes32 a, bytes32 b, string memory err) internal { + assertEq(a, b, err); + } + + function assertEq(int a, int b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + fail(); + } + } + function assertEq(int a, int b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEq(uint a, uint b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + fail(); + } + } + function assertEq(uint a, uint b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEqDecimal(int a, int b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal int]"); + emit log_named_decimal_int(" Expected", b, decimals); + emit log_named_decimal_int(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + function assertEqDecimal(uint a, uint b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Expected", b, decimals); + emit log_named_decimal_uint(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + + function assertGt(uint a, uint b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGt(uint a, uint b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGt(int a, int b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGt(int a, int b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGtDecimal(int a, int b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + function assertGtDecimal(uint a, uint b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + + function assertGe(uint a, uint b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGe(uint a, uint b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGe(int a, int b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGe(int a, int b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGeDecimal(int a, int b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + function assertGeDecimal(uint a, uint b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertLt(uint a, uint b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLt(uint a, uint b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLt(int a, int b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLt(int a, int b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLtDecimal(int a, int b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + function assertLtDecimal(uint a, uint b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + + function assertLe(uint a, uint b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLe(uint a, uint b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLe(int a, int b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLe(int a, int b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLeDecimal(int a, int b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } + } + function assertLeDecimal(uint a, uint b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log("Error: a == b not satisfied [string]"); + emit log_named_string(" Expected", b); + emit log_named_string(" Actual", a); + fail(); + } + } + function assertEq(string memory a, string memory b, string memory err) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { + ok = true; + if (a.length == b.length) { + for (uint i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + ok = false; + } + } + } else { + ok = false; + } + } + function assertEq0(bytes memory a, bytes memory b) internal { + if (!checkEq0(a, b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Expected", b); + emit log_named_bytes(" Actual", a); + fail(); + } + } + function assertEq0(bytes memory a, bytes memory b, string memory err) internal { + if (!checkEq0(a, b)) { + emit log_named_string("Error", err); + assertEq0(a, b); + } + } +} diff --git a/lib/forge-std/.github/workflows/ci.yml b/lib/forge-std/.github/workflows/ci.yml new file mode 100644 index 0000000..96b2336 --- /dev/null +++ b/lib/forge-std/.github/workflows/ci.yml @@ -0,0 +1,92 @@ +name: CI + +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Print forge version + run: forge --version + + # Backwards compatibility checks. + - name: Check compatibility with 0.8.0 + if: always() + run: forge build --skip test --use solc:0.8.0 + + - name: Check compatibility with 0.7.6 + if: always() + run: forge build --skip test --use solc:0.7.6 + + - name: Check compatibility with 0.7.0 + if: always() + run: forge build --skip test --use solc:0.7.0 + + - name: Check compatibility with 0.6.12 + if: always() + run: forge build --skip test --use solc:0.6.12 + + - name: Check compatibility with 0.6.2 + if: always() + run: forge build --skip test --use solc:0.6.2 + + # via-ir compilation time checks. + - name: Measure compilation time of Test with 0.8.17 --via-ir + if: always() + run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir + + - name: Measure compilation time of TestBase with 0.8.17 --via-ir + if: always() + run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir + + - name: Measure compilation time of Script with 0.8.17 --via-ir + if: always() + run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir + + - name: Measure compilation time of ScriptBase with 0.8.17 --via-ir + if: always() + run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Print forge version + run: forge --version + + - name: Run tests + run: forge test -vvv + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Print forge version + run: forge --version + + - name: Check formatting + run: forge fmt --check diff --git a/lib/forge-std/.gitignore b/lib/forge-std/.gitignore new file mode 100644 index 0000000..756106d --- /dev/null +++ b/lib/forge-std/.gitignore @@ -0,0 +1,4 @@ +cache/ +out/ +.vscode +.idea diff --git a/lib/forge-std/.gitmodules b/lib/forge-std/.gitmodules new file mode 100644 index 0000000..e124719 --- /dev/null +++ b/lib/forge-std/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/ds-test"] + path = lib/ds-test + url = https://github.com/dapphub/ds-test diff --git a/lib/forge-std/LICENSE-APACHE b/lib/forge-std/LICENSE-APACHE new file mode 100644 index 0000000..cf01a49 --- /dev/null +++ b/lib/forge-std/LICENSE-APACHE @@ -0,0 +1,203 @@ +Copyright Contributors to Forge Standard Library + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/forge-std/LICENSE-MIT b/lib/forge-std/LICENSE-MIT new file mode 100644 index 0000000..28f9830 --- /dev/null +++ b/lib/forge-std/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright Contributors to Forge Standard Library + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER +DEALINGS IN THE SOFTWARE.R diff --git a/lib/forge-std/README.md b/lib/forge-std/README.md new file mode 100644 index 0000000..8494a7d --- /dev/null +++ b/lib/forge-std/README.md @@ -0,0 +1,250 @@ +# Forge Standard Library • [![CI status](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml/badge.svg)](https://github.com/foundry-rs/forge-std/actions/workflows/ci.yml) + +Forge Standard Library is a collection of helpful contracts and libraries for use with [Forge and Foundry](https://github.com/foundry-rs/foundry). It leverages Forge's cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes. + +**Learn how to use Forge-Std with the [📖 Foundry Book (Forge-Std Guide)](https://book.getfoundry.sh/forge/forge-std.html).** + +## Install + +```bash +forge install foundry-rs/forge-std +``` + +## Contracts +### stdError + +This is a helper contract for errors and reverts. In Forge, this contract is particularly helpful for the `expectRevert` cheatcode, as it provides all compiler builtin errors. + +See the contract itself for all error codes. + +#### Example usage + +```solidity + +import "forge-std/Test.sol"; + +contract TestContract is Test { + ErrorsTest test; + + function setUp() public { + test = new ErrorsTest(); + } + + function testExpectArithmetic() public { + vm.expectRevert(stdError.arithmeticError); + test.arithmeticError(10); + } +} + +contract ErrorsTest { + function arithmeticError(uint256 a) public { + uint256 a = a - 100; + } +} +``` + +### stdStorage + +This is a rather large contract due to all of the overloading to make the UX decent. Primarily, it is a wrapper around the `record` and `accesses` cheatcodes. It can *always* find and write the storage slot(s) associated with a particular variable without knowing the storage layout. The one _major_ caveat to this is while a slot can be found for packed storage variables, we can't write to that variable safely. If a user tries to write to a packed slot, the execution throws an error, unless it is uninitialized (`bytes32(0)`). + +This works by recording all `SLOAD`s and `SSTORE`s during a function call. If there is a single slot read or written to, it immediately returns the slot. Otherwise, behind the scenes, we iterate through and check each one (assuming the user passed in a `depth` parameter). If the variable is a struct, you can pass in a `depth` parameter which is basically the field depth. + +I.e.: +```solidity +struct T { + // depth 0 + uint256 a; + // depth 1 + uint256 b; +} +``` + +#### Example usage + +```solidity +import "forge-std/Test.sol"; + +contract TestContract is Test { + using stdStorage for StdStorage; + + Storage test; + + function setUp() public { + test = new Storage(); + } + + function testFindExists() public { + // Lets say we want to find the slot for the public + // variable `exists`. We just pass in the function selector + // to the `find` command + uint256 slot = stdstore.target(address(test)).sig("exists()").find(); + assertEq(slot, 0); + } + + function testWriteExists() public { + // Lets say we want to write to the slot for the public + // variable `exists`. We just pass in the function selector + // to the `checked_write` command + stdstore.target(address(test)).sig("exists()").checked_write(100); + assertEq(test.exists(), 100); + } + + // It supports arbitrary storage layouts, like assembly based storage locations + function testFindHidden() public { + // `hidden` is a random hash of a bytes, iteration through slots would + // not find it. Our mechanism does + // Also, you can use the selector instead of a string + uint256 slot = stdstore.target(address(test)).sig(test.hidden.selector).find(); + assertEq(slot, uint256(keccak256("my.random.var"))); + } + + // If targeting a mapping, you have to pass in the keys necessary to perform the find + // i.e.: + function testFindMapping() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_addr.selector) + .with_key(address(this)) + .find(); + // in the `Storage` constructor, we wrote that this address' value was 1 in the map + // so when we load the slot, we expect it to be 1 + assertEq(uint(vm.load(address(test), bytes32(slot))), 1); + } + + // If the target is a struct, you can specify the field depth: + function testFindStruct() public { + // NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc. + uint256 slot_for_a_field = stdstore + .target(address(test)) + .sig(test.basicStruct.selector) + .depth(0) + .find(); + + uint256 slot_for_b_field = stdstore + .target(address(test)) + .sig(test.basicStruct.selector) + .depth(1) + .find(); + + assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1); + assertEq(uint(vm.load(address(test), bytes32(slot_for_b_field))), 2); + } +} + +// A complex storage contract +contract Storage { + struct UnpackedStruct { + uint256 a; + uint256 b; + } + + constructor() { + map_addr[msg.sender] = 1; + } + + uint256 public exists = 1; + mapping(address => uint256) public map_addr; + // mapping(address => Packed) public map_packed; + mapping(address => UnpackedStruct) public map_struct; + mapping(address => mapping(address => uint256)) public deep_map; + mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; + UnpackedStruct public basicStruct = UnpackedStruct({ + a: 1, + b: 2 + }); + + function hidden() public view returns (bytes32 t) { + // an extremely hidden storage slot + bytes32 slot = keccak256("my.random.var"); + assembly { + t := sload(slot) + } + } +} +``` + +### stdCheats + +This is a wrapper over miscellaneous cheatcodes that need wrappers to be more dev friendly. Currently there are only functions related to `prank`. In general, users may expect ETH to be put into an address on `prank`, but this is not the case for safety reasons. Explicitly this `hoax` function should only be used for address that have expected balances as it will get overwritten. If an address already has ETH, you should just use `prank`. If you want to change that balance explicitly, just use `deal`. If you want to do both, `hoax` is also right for you. + + +#### Example usage: +```solidity + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +// Inherit the stdCheats +contract StdCheatsTest is Test { + Bar test; + function setUp() public { + test = new Bar(); + } + + function testHoax() public { + // we call `hoax`, which gives the target address + // eth and then calls `prank` + hoax(address(1337)); + test.bar{value: 100}(address(1337)); + + // overloaded to allow you to specify how much eth to + // initialize the address with + hoax(address(1337), 1); + test.bar{value: 1}(address(1337)); + } + + function testStartHoax() public { + // we call `startHoax`, which gives the target address + // eth and then calls `startPrank` + // + // it is also overloaded so that you can specify an eth amount + startHoax(address(1337)); + test.bar{value: 100}(address(1337)); + test.bar{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } +} + +contract Bar { + function bar(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + } +} +``` + +### Std Assertions + +Expand upon the assertion functions from the `DSTest` library. + +### `console.log` + +Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log). +It's recommended to use `console2.sol` as shown below, as this will show the decoded logs in Forge traces. + +```solidity +// import it indirectly via Test.sol +import "forge-std/Test.sol"; +// or directly import it +import "forge-std/console2.sol"; +... +console2.log(someValue); +``` + +If you need compatibility with Hardhat, you must use the standard `console.sol` instead. +Due to a bug in `console.sol`, logs that use `uint256` or `int256` types will not be properly decoded in Forge traces. + +```solidity +// import it indirectly via Test.sol +import "forge-std/Test.sol"; +// or directly import it +import "forge-std/console.sol"; +... +console.log(someValue); +``` + +## License + +Forge Standard Library is offered under either [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-APACHE) license. diff --git a/lib/forge-std/foundry.toml b/lib/forge-std/foundry.toml new file mode 100644 index 0000000..cca8301 --- /dev/null +++ b/lib/forge-std/foundry.toml @@ -0,0 +1,21 @@ +[profile.default] +fs_permissions = [{ access = "read-write", path = "./"}] + +[rpc_endpoints] +# The RPC URLs are modified versions of the default for testing initialization. +mainnet = "https://mainnet.infura.io/v3/7a8769b798b642f6933f2ed52042bd70" # Different API key. +optimism_goerli = "https://goerli.optimism.io/" # Adds a trailing slash. +arbitrum_one_goerli = "https://goerli-rollup.arbitrum.io/rpc/" # Adds a trailing slash. +needs_undefined_env_var = "${UNDEFINED_RPC_URL_PLACEHOLDER}" + +[fmt] +# These are all the `forge fmt` defaults. +line_length = 120 +tab_width = 4 +bracket_spacing = false +int_types = 'long' +multiline_func_header = 'attributes_first' +quote_style = 'double' +number_underscore = 'preserve' +single_line_statement_blocks = 'preserve' +ignore = ["src/console.sol", "src/console2.sol"] \ No newline at end of file diff --git a/lib/forge-std/lib/ds-test/.gitignore b/lib/forge-std/lib/ds-test/.gitignore new file mode 100644 index 0000000..63f0b2c --- /dev/null +++ b/lib/forge-std/lib/ds-test/.gitignore @@ -0,0 +1,3 @@ +/.dapple +/build +/out diff --git a/lib/forge-std/lib/ds-test/LICENSE b/lib/forge-std/lib/ds-test/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/forge-std/lib/ds-test/LICENSE @@ -0,0 +1,674 @@ + 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/lib/forge-std/lib/ds-test/Makefile b/lib/forge-std/lib/ds-test/Makefile new file mode 100644 index 0000000..661dac4 --- /dev/null +++ b/lib/forge-std/lib/ds-test/Makefile @@ -0,0 +1,14 @@ +all:; dapp build + +test: + -dapp --use solc:0.4.23 build + -dapp --use solc:0.4.26 build + -dapp --use solc:0.5.17 build + -dapp --use solc:0.6.12 build + -dapp --use solc:0.7.5 build + +demo: + DAPP_SRC=demo dapp --use solc:0.7.5 build + -hevm dapp-test --verbose 3 + +.PHONY: test demo diff --git a/lib/forge-std/lib/ds-test/default.nix b/lib/forge-std/lib/ds-test/default.nix new file mode 100644 index 0000000..cf65419 --- /dev/null +++ b/lib/forge-std/lib/ds-test/default.nix @@ -0,0 +1,4 @@ +{ solidityPackage, dappsys }: solidityPackage { + name = "ds-test"; + src = ./src; +} diff --git a/lib/forge-std/lib/ds-test/demo/demo.sol b/lib/forge-std/lib/ds-test/demo/demo.sol new file mode 100644 index 0000000..f3bb48e --- /dev/null +++ b/lib/forge-std/lib/ds-test/demo/demo.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.5.0; + +import "../src/test.sol"; + +contract DemoTest is DSTest { + function test_this() public pure { + require(true); + } + function test_logs() public { + emit log("-- log(string)"); + emit log("a string"); + + emit log("-- log_named_uint(string, uint)"); + emit log_named_uint("uint", 512); + + emit log("-- log_named_int(string, int)"); + emit log_named_int("int", -512); + + emit log("-- log_named_address(string, address)"); + emit log_named_address("address", address(this)); + + emit log("-- log_named_bytes32(string, bytes32)"); + emit log_named_bytes32("bytes32", "a string"); + + emit log("-- log_named_bytes(string, bytes)"); + emit log_named_bytes("bytes", hex"cafefe"); + + emit log("-- log_named_string(string, string)"); + emit log_named_string("string", "a string"); + + emit log("-- log_named_decimal_uint(string, uint, uint)"); + emit log_named_decimal_uint("decimal uint", 1.0e18, 18); + + emit log("-- log_named_decimal_int(string, int, uint)"); + emit log_named_decimal_int("decimal int", -1.0e18, 18); + } + event log_old_named_uint(bytes32,uint); + function test_old_logs() public { + emit log_old_named_uint("key", 500); + emit log_named_bytes32("bkey", "val"); + } + function test_trace() public view { + this.echo("string 1", "string 2"); + } + function test_multiline() public { + emit log("a multiline\\nstring"); + emit log("a multiline string"); + emit log_bytes("a string"); + emit log_bytes("a multiline\nstring"); + emit log_bytes("a multiline\\nstring"); + emit logs(hex"0000"); + emit log_named_bytes("0x0000", hex"0000"); + emit logs(hex"ff"); + } + function echo(string memory s1, string memory s2) public pure + returns (string memory, string memory) + { + return (s1, s2); + } + + function prove_this(uint x) public { + emit log_named_uint("sym x", x); + assertGt(x + 1, 0); + } + + function test_logn() public { + assembly { + log0(0x01, 0x02) + log1(0x01, 0x02, 0x03) + log2(0x01, 0x02, 0x03, 0x04) + log3(0x01, 0x02, 0x03, 0x04, 0x05) + } + } + + event MyEvent(uint, uint indexed, uint, uint indexed); + function test_events() public { + emit MyEvent(1, 2, 3, 4); + } + + function test_asserts() public { + string memory err = "this test has failed!"; + emit log("## assertTrue(bool)\n"); + assertTrue(false); + emit log("\n"); + assertTrue(false, err); + + emit log("\n## assertEq(address,address)\n"); + assertEq(address(this), msg.sender); + emit log("\n"); + assertEq(address(this), msg.sender, err); + + emit log("\n## assertEq32(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(uint,uint)\n"); + assertEq(uint(0), 1); + emit log("\n"); + assertEq(uint(0), 1, err); + + emit log("\n## assertEq(int,int)\n"); + assertEq(-1, -2); + emit log("\n"); + assertEq(-1, -2, err); + + emit log("\n## assertEqDecimal(int,int,uint)\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertEqDecimal(uint,uint,uint)\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGt(uint,uint)\n"); + assertGt(uint(0), 0); + emit log("\n"); + assertGt(uint(0), 0, err); + + emit log("\n## assertGt(int,int)\n"); + assertGt(-1, -1); + emit log("\n"); + assertGt(-1, -1, err); + + emit log("\n## assertGtDecimal(int,int,uint)\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGtDecimal(uint,uint,uint)\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGe(uint,uint)\n"); + assertGe(uint(0), 1); + emit log("\n"); + assertGe(uint(0), 1, err); + + emit log("\n## assertGe(int,int)\n"); + assertGe(-1, 0); + emit log("\n"); + assertGe(-1, 0, err); + + emit log("\n## assertGeDecimal(int,int,uint)\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGeDecimal(uint,uint,uint)\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertLt(uint,uint)\n"); + assertLt(uint(0), 0); + emit log("\n"); + assertLt(uint(0), 0, err); + + emit log("\n## assertLt(int,int)\n"); + assertLt(-1, -1); + emit log("\n"); + assertLt(-1, -1, err); + + emit log("\n## assertLtDecimal(int,int,uint)\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLtDecimal(uint,uint,uint)\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertLe(uint,uint)\n"); + assertLe(uint(1), 0); + emit log("\n"); + assertLe(uint(1), 0, err); + + emit log("\n## assertLe(int,int)\n"); + assertLe(0, -1); + emit log("\n"); + assertLe(0, -1, err); + + emit log("\n## assertLeDecimal(int,int,uint)\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLeDecimal(uint,uint,uint)\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertEq(string,string)\n"); + string memory s1 = "string 1"; + string memory s2 = "string 2"; + assertEq(s1, s2); + emit log("\n"); + assertEq(s1, s2, err); + + emit log("\n## assertEq0(bytes,bytes)\n"); + assertEq0(hex"abcdef01", hex"abcdef02"); + emit log("\n"); + assertEq0(hex"abcdef01", hex"abcdef02", err); + } +} + +contract DemoTestWithSetUp { + function setUp() public { + } + function test_pass() public pure { + } +} diff --git a/lib/forge-std/lib/ds-test/package.json b/lib/forge-std/lib/ds-test/package.json new file mode 100644 index 0000000..4802ada --- /dev/null +++ b/lib/forge-std/lib/ds-test/package.json @@ -0,0 +1,15 @@ +{ + "name": "ds-test", + "version": "1.0.0", + "description": "Assertions, equality checks and other test helpers ", + "bugs": "https://github.com/dapphub/ds-test/issues", + "license": "GPL-3.0", + "author": "Contributors to ds-test", + "files": [ + "src/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/dapphub/ds-test.git" + } +} diff --git a/lib/forge-std/lib/ds-test/src/test.sol b/lib/forge-std/lib/ds-test/src/test.sol new file mode 100644 index 0000000..515a3bd --- /dev/null +++ b/lib/forge-std/lib/ds-test/src/test.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// 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 . + +pragma solidity >=0.5.0; + +contract DSTest { + event log (string); + event logs (bytes); + + event log_address (address); + event log_bytes32 (bytes32); + event log_int (int); + event log_uint (uint); + event log_bytes (bytes); + event log_string (string); + + event log_named_address (string key, address val); + event log_named_bytes32 (string key, bytes32 val); + event log_named_decimal_int (string key, int val, uint decimals); + event log_named_decimal_uint (string key, uint val, uint decimals); + event log_named_int (string key, int val); + event log_named_uint (string key, uint val); + event log_named_bytes (string key, bytes val); + event log_named_string (string key, string val); + + bool public IS_TEST = true; + bool private _failed; + + address constant HEVM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + modifier mayRevert() { _; } + modifier testopts(string memory) { _; } + + function failed() public returns (bool) { + if (_failed) { + return _failed; + } else { + bool globalFailed = false; + if (hasHEVMContext()) { + (, bytes memory retdata) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("load(address,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed")) + ) + ); + globalFailed = abi.decode(retdata, (bool)); + } + return globalFailed; + } + } + + function fail() internal { + if (hasHEVMContext()) { + (bool status, ) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("store(address,bytes32,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) + ) + ); + status; // Silence compiler warnings + } + _failed = true; + } + + function hasHEVMContext() internal view returns (bool) { + uint256 hevmCodeSize = 0; + assembly { + hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) + } + return hevmCodeSize > 0; + } + + modifier logs_gas() { + uint startGas = gasleft(); + _; + uint endGas = gasleft(); + emit log_named_uint("gas", startGas - endGas); + } + + function assertTrue(bool condition) internal { + if (!condition) { + emit log("Error: Assertion Failed"); + fail(); + } + } + + function assertTrue(bool condition, string memory err) internal { + if (!condition) { + emit log_named_string("Error", err); + assertTrue(condition); + } + } + + function assertEq(address a, address b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [address]"); + emit log_named_address(" Expected", b); + emit log_named_address(" Actual", a); + fail(); + } + } + function assertEq(address a, address b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes32 a, bytes32 b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [bytes32]"); + emit log_named_bytes32(" Expected", b); + emit log_named_bytes32(" Actual", a); + fail(); + } + } + function assertEq(bytes32 a, bytes32 b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + function assertEq32(bytes32 a, bytes32 b) internal { + assertEq(a, b); + } + function assertEq32(bytes32 a, bytes32 b, string memory err) internal { + assertEq(a, b, err); + } + + function assertEq(int a, int b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + fail(); + } + } + function assertEq(int a, int b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEq(uint a, uint b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + fail(); + } + } + function assertEq(uint a, uint b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEqDecimal(int a, int b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal int]"); + emit log_named_decimal_int(" Expected", b, decimals); + emit log_named_decimal_int(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + function assertEqDecimal(uint a, uint b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Expected", b, decimals); + emit log_named_decimal_uint(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + + function assertGt(uint a, uint b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGt(uint a, uint b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGt(int a, int b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGt(int a, int b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGtDecimal(int a, int b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + function assertGtDecimal(uint a, uint b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + + function assertGe(uint a, uint b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGe(uint a, uint b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGe(int a, int b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGe(int a, int b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGeDecimal(int a, int b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + function assertGeDecimal(uint a, uint b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertLt(uint a, uint b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLt(uint a, uint b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLt(int a, int b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLt(int a, int b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLtDecimal(int a, int b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + function assertLtDecimal(uint a, uint b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + + function assertLe(uint a, uint b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLe(uint a, uint b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLe(int a, int b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLe(int a, int b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLeDecimal(int a, int b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } + } + function assertLeDecimal(uint a, uint b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log("Error: a == b not satisfied [string]"); + emit log_named_string(" Expected", b); + emit log_named_string(" Actual", a); + fail(); + } + } + function assertEq(string memory a, string memory b, string memory err) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { + ok = true; + if (a.length == b.length) { + for (uint i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + ok = false; + } + } + } else { + ok = false; + } + } + function assertEq0(bytes memory a, bytes memory b) internal { + if (!checkEq0(a, b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Expected", b); + emit log_named_bytes(" Actual", a); + fail(); + } + } + function assertEq0(bytes memory a, bytes memory b, string memory err) internal { + if (!checkEq0(a, b)) { + emit log_named_string("Error", err); + assertEq0(a, b); + } + } +} diff --git a/lib/forge-std/package.json b/lib/forge-std/package.json new file mode 100644 index 0000000..a000ac8 --- /dev/null +++ b/lib/forge-std/package.json @@ -0,0 +1,16 @@ +{ + "name": "forge-std", + "version": "1.2.0", + "description": "Forge Standard Library is a collection of helpful contracts and libraries for use with Forge and Foundry.", + "homepage": "https://book.getfoundry.sh/forge/forge-std", + "bugs": "https://github.com/foundry-rs/forge-std/issues", + "license": "(Apache-2.0 OR MIT)", + "author": "Contributors to Forge Standard Library", + "files": [ + "src/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/foundry-rs/forge-std.git" + } +} diff --git a/lib/forge-std/src/Base.sol b/lib/forge-std/src/Base.sol new file mode 100644 index 0000000..e0d8b05 --- /dev/null +++ b/lib/forge-std/src/Base.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +import {StdStorage} from "./StdStorage.sol"; +import {Vm, VmSafe} from "./Vm.sol"; + +abstract contract CommonBase { + // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. + address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); + // console.sol and console2.sol work by executing a staticcall to this address. + address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67; + // Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. + address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller")))); + // Address of the test contract, deployed by the DEFAULT_SENDER. + address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; + + uint256 internal constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; + + Vm internal constant vm = Vm(VM_ADDRESS); + StdStorage internal stdstore; +} + +abstract contract TestBase is CommonBase {} + +abstract contract ScriptBase is CommonBase { + // Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy. + address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + + VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS); +} diff --git a/lib/forge-std/src/Script.sol b/lib/forge-std/src/Script.sol new file mode 100644 index 0000000..bffccad --- /dev/null +++ b/lib/forge-std/src/Script.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +// 💬 ABOUT +// Standard Library's default Script. + +// 🧩 MODULES +import {ScriptBase} from "./Base.sol"; +import {console} from "./console.sol"; +import {console2} from "./console2.sol"; +import {StdChains} from "./StdChains.sol"; +import {StdCheatsSafe} from "./StdCheats.sol"; +import {stdJson} from "./StdJson.sol"; +import {stdMath} from "./StdMath.sol"; +import {StdStorage, stdStorageSafe} from "./StdStorage.sol"; +import {StdUtils} from "./StdUtils.sol"; +import {VmSafe} from "./Vm.sol"; + +// 📦 BOILERPLATE +import {ScriptBase} from "./Base.sol"; + +// ⭐️ SCRIPT +abstract contract Script is StdChains, StdCheatsSafe, StdUtils, ScriptBase { + // Note: IS_SCRIPT() must return true. + bool public IS_SCRIPT = true; +} diff --git a/lib/forge-std/src/StdAssertions.sol b/lib/forge-std/src/StdAssertions.sol new file mode 100644 index 0000000..733dbba --- /dev/null +++ b/lib/forge-std/src/StdAssertions.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +import {DSTest} from "ds-test/test.sol"; +import {stdMath} from "./StdMath.sol"; + +abstract contract StdAssertions is DSTest { + event log_array(uint256[] val); + event log_array(int256[] val); + event log_array(address[] val); + event log_named_array(string key, uint256[] val); + event log_named_array(string key, int256[] val); + event log_named_array(string key, address[] val); + + function fail(string memory err) internal virtual { + emit log_named_string("Error", err); + fail(); + } + + function assertFalse(bool data) internal virtual { + assertTrue(!data); + } + + function assertFalse(bool data, string memory err) internal virtual { + assertTrue(!data, err); + } + + function assertEq(bool a, bool b) internal virtual { + if (a != b) { + emit log("Error: a == b not satisfied [bool]"); + emit log_named_string(" Expected", b ? "true" : "false"); + emit log_named_string(" Actual", a ? "true" : "false"); + fail(); + } + } + + function assertEq(bool a, bool b, string memory err) internal virtual { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes memory a, bytes memory b) internal virtual { + assertEq0(a, b); + } + + function assertEq(bytes memory a, bytes memory b, string memory err) internal virtual { + assertEq0(a, b, err); + } + + function assertEq(uint256[] memory a, uint256[] memory b) internal virtual { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [uint[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(int256[] memory a, int256[] memory b) internal virtual { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [int[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(address[] memory a, address[] memory b) internal virtual { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [address[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(uint256[] memory a, uint256[] memory b, string memory err) internal virtual { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(int256[] memory a, int256[] memory b, string memory err) internal virtual { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(address[] memory a, address[] memory b, string memory err) internal virtual { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + // Legacy helper + function assertEqUint(uint256 a, uint256 b) internal virtual { + assertEq(uint256(a), uint256(b)); + } + + function assertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log("Error: a ~= b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + emit log_named_uint(" Max Delta", maxDelta); + emit log_named_uint(" Delta", delta); + fail(); + } + } + + function assertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log_named_string("Error", err); + assertApproxEqAbs(a, b, maxDelta); + } + } + + function assertApproxEqAbs(int256 a, int256 b, uint256 maxDelta) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log("Error: a ~= b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + emit log_named_uint(" Max Delta", maxDelta); + emit log_named_uint(" Delta", delta); + fail(); + } + } + + function assertApproxEqAbs(int256 a, int256 b, uint256 maxDelta, string memory err) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log_named_string("Error", err); + assertApproxEqAbs(a, b, maxDelta); + } + } + + function assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log("Error: a ~= b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint(" % Delta", percentDelta, 18); + fail(); + } + } + + function assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100% + string memory err + ) internal virtual { + if (b == 0) return assertEq(a, b, err); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log_named_string("Error", err); + assertApproxEqRel(a, b, maxPercentDelta); + } + } + + function assertApproxEqRel(int256 a, int256 b, uint256 maxPercentDelta) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log("Error: a ~= b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint(" % Delta", percentDelta, 18); + fail(); + } + } + + function assertApproxEqRel(int256 a, int256 b, uint256 maxPercentDelta, string memory err) internal virtual { + if (b == 0) return assertEq(a, b, err); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log_named_string("Error", err); + assertApproxEqRel(a, b, maxPercentDelta); + } + } +} diff --git a/lib/forge-std/src/StdChains.sol b/lib/forge-std/src/StdChains.sol new file mode 100644 index 0000000..68755bf --- /dev/null +++ b/lib/forge-std/src/StdChains.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +import {VmSafe} from "./Vm.sol"; + +/** + * StdChains provides information about EVM compatible chains that can be used in scripts/tests. + * For each chain, the chain's name, chain ID, and a default RPC URL are provided. Chains are + * identified by their alias, which is the same as the alias in the `[rpc_endpoints]` section of + * the `foundry.toml` file. For best UX, ensure the alias in the `foundry.toml` file match the + * alias used in this contract, which can be found as the first argument to the + * `setChainWithDefaultRpcUrl` call in the `initialize` function. + * + * There are two main ways to use this contract: + * 1. Set a chain with `setChain(string memory chainAlias, Chain memory chain)` + * 2. Get a chain with `getChain(string memory chainAlias)` or `getChain(uint256 chainId)`. + * + * The first time either of those are used, chains are initialized with the default set of RPC URLs. + * This is done in `initialize`, which uses `setChainWithDefaultRpcUrl`. Defaults are recorded in + * `defaultRpcUrls`. + * + * The `setChain` function is straightforward, and it simply saves off the given chain data. + * + * The `getChain` methods use `getChainWithUpdatedRpcUrl` to return a chain. For example, let's say + * we want to retrieve `mainnet`'s RPC URL: + * - If you haven't set any mainnet chain info with `setChain` and you haven't specified that + * chain in `foundry.toml`, the default data and RPC URL will be returned. + * - If you have set a mainnet RPC URL in `foundry.toml` it will return that, if valid (e.g. if + * a URL is given or if an environment variable is given and that environment variable exists). + * Otherwise, the default data is returned. + * - If you specified data with `setChain` it will return that. + * + * Summarizing the above, the prioritization hierarchy is `setChain` -> `foundry.toml` -> defaults. + */ +abstract contract StdChains { + VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); + + bool private initialized; + + struct Chain { + // The chain name. + string name; + // The chain's Chain ID. + uint256 chainId; + // A default RPC endpoint for this chain. + // NOTE: This default RPC URL is included for convenience to facilitate quick tests and + // experimentation. Do not use this RPC URL for production test suites, CI, or other heavy + // usage as you will be throttled and this is a disservice to others who need this endpoint. + string rpcUrl; + } + + // Maps from the chain's alias (matching the alias in the `foundry.toml` file) to chain data. + mapping(string => Chain) private chains; + // Maps from the chain's alias to it's default RPC URL. + mapping(string => string) private defaultRpcUrls; + // Maps from a chain ID to it's alias. + mapping(uint256 => string) private idToAlias; + + // The RPC URL will be fetched from config or defaultRpcUrls if possible. + function getChain(string memory chainAlias) internal virtual returns (Chain memory chain) { + require(bytes(chainAlias).length != 0, "StdChains getChain(string): Chain alias cannot be the empty string."); + + initialize(); + chain = chains[chainAlias]; + require( + chain.chainId != 0, + string(abi.encodePacked("StdChains getChain(string): Chain with alias \"", chainAlias, "\" not found.")) + ); + + chain = getChainWithUpdatedRpcUrl(chainAlias, chain); + } + + function getChain(uint256 chainId) internal virtual returns (Chain memory chain) { + require(chainId != 0, "StdChains getChain(uint256): Chain ID cannot be 0."); + initialize(); + string memory chainAlias = idToAlias[chainId]; + + chain = chains[chainAlias]; + + require( + chain.chainId != 0, + string(abi.encodePacked("StdChains getChain(uint256): Chain with ID ", vm.toString(chainId), " not found.")) + ); + + chain = getChainWithUpdatedRpcUrl(chainAlias, chain); + } + + // set chain info, with priority to argument's rpcUrl field. + function setChain(string memory chainAlias, Chain memory chain) internal virtual { + require( + bytes(chainAlias).length != 0, "StdChains setChain(string,Chain): Chain alias cannot be the empty string." + ); + + require(chain.chainId != 0, "StdChains setChain(string,Chain): Chain ID cannot be 0."); + + initialize(); + string memory foundAlias = idToAlias[chain.chainId]; + + require( + bytes(foundAlias).length == 0 || keccak256(bytes(foundAlias)) == keccak256(bytes(chainAlias)), + string( + abi.encodePacked( + "StdChains setChain(string,Chain): Chain ID ", + vm.toString(chain.chainId), + " already used by \"", + foundAlias, + "\"." + ) + ) + ); + + uint256 oldChainId = chains[chainAlias].chainId; + delete idToAlias[oldChainId]; + + chains[chainAlias] = chain; + idToAlias[chain.chainId] = chainAlias; + } + + // lookup rpcUrl, in descending order of priority: + // current -> config (foundry.toml) -> default + function getChainWithUpdatedRpcUrl(string memory chainAlias, Chain memory chain) + private + view + returns (Chain memory) + { + if (bytes(chain.rpcUrl).length == 0) { + try vm.rpcUrl(chainAlias) returns (string memory configRpcUrl) { + chain.rpcUrl = configRpcUrl; + } catch (bytes memory err) { + chain.rpcUrl = defaultRpcUrls[chainAlias]; + // distinguish 'not found' from 'cannot read' + bytes memory notFoundError = + abi.encodeWithSignature("CheatCodeError", string(abi.encodePacked("invalid rpc url ", chainAlias))); + if (keccak256(notFoundError) != keccak256(err) || bytes(chain.rpcUrl).length == 0) { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, err), mload(err)) + } + } + } + } + return chain; + } + + function initialize() private { + if (initialized) return; + + initialized = true; + + // If adding an RPC here, make sure to test the default RPC URL in `testRpcs` + setChainWithDefaultRpcUrl("anvil", Chain("Anvil", 31337, "http://127.0.0.1:8545")); + setChainWithDefaultRpcUrl( + "mainnet", Chain("Mainnet", 1, "https://mainnet.infura.io/v3/6770454bc6ea42c58aac12978531b93f") + ); + setChainWithDefaultRpcUrl( + "goerli", Chain("Goerli", 5, "https://goerli.infura.io/v3/6770454bc6ea42c58aac12978531b93f") + ); + setChainWithDefaultRpcUrl( + "sepolia", Chain("Sepolia", 11155111, "https://sepolia.infura.io/v3/6770454bc6ea42c58aac12978531b93f") + ); + setChainWithDefaultRpcUrl("optimism", Chain("Optimism", 10, "https://mainnet.optimism.io")); + setChainWithDefaultRpcUrl("optimism_goerli", Chain("Optimism Goerli", 420, "https://goerli.optimism.io")); + setChainWithDefaultRpcUrl("arbitrum_one", Chain("Arbitrum One", 42161, "https://arb1.arbitrum.io/rpc")); + setChainWithDefaultRpcUrl( + "arbitrum_one_goerli", Chain("Arbitrum One Goerli", 421613, "https://goerli-rollup.arbitrum.io/rpc") + ); + setChainWithDefaultRpcUrl("arbitrum_nova", Chain("Arbitrum Nova", 42170, "https://nova.arbitrum.io/rpc")); + setChainWithDefaultRpcUrl("polygon", Chain("Polygon", 137, "https://polygon-rpc.com")); + setChainWithDefaultRpcUrl("polygon_mumbai", Chain("Polygon Mumbai", 80001, "https://rpc-mumbai.maticvigil.com")); + setChainWithDefaultRpcUrl("avalanche", Chain("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc")); + setChainWithDefaultRpcUrl( + "avalanche_fuji", Chain("Avalanche Fuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc") + ); + setChainWithDefaultRpcUrl("bnb_smart_chain", Chain("BNB Smart Chain", 56, "https://bsc-dataseed1.binance.org")); + setChainWithDefaultRpcUrl("bnb_smart_chain_testnet", Chain("BNB Smart Chain Testnet", 97, "https://data-seed-prebsc-1-s1.binance.org:8545"));// forgefmt: disable-line + setChainWithDefaultRpcUrl("gnosis_chain", Chain("Gnosis Chain", 100, "https://rpc.gnosischain.com")); + } + + // set chain info, with priority to chainAlias' rpc url in foundry.toml + function setChainWithDefaultRpcUrl(string memory chainAlias, Chain memory chain) private { + string memory rpcUrl = chain.rpcUrl; + defaultRpcUrls[chainAlias] = rpcUrl; + chain.rpcUrl = ""; + setChain(chainAlias, chain); + chain.rpcUrl = rpcUrl; // restore argument + } +} diff --git a/lib/forge-std/src/StdCheats.sol b/lib/forge-std/src/StdCheats.sol new file mode 100644 index 0000000..f7204a5 --- /dev/null +++ b/lib/forge-std/src/StdCheats.sol @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +import {StdStorage, stdStorage} from "./StdStorage.sol"; +import {Vm} from "./Vm.sol"; + +abstract contract StdCheatsSafe { + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + bool private gasMeteringOff; + + // Data structures to parse Transaction objects from the broadcast artifact + // that conform to EIP1559. The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct RawTx1559 { + string[] arguments; + address contractAddress; + string contractName; + // json value name = function + string functionSig; + bytes32 hash; + // json value name = tx + RawTx1559Detail txDetail; + // json value name = type + string opcode; + } + + struct RawTx1559Detail { + AccessList[] accessList; + bytes data; + address from; + bytes gas; + bytes nonce; + address to; + bytes txType; + bytes value; + } + + struct Tx1559 { + string[] arguments; + address contractAddress; + string contractName; + string functionSig; + bytes32 hash; + Tx1559Detail txDetail; + string opcode; + } + + struct Tx1559Detail { + AccessList[] accessList; + bytes data; + address from; + uint256 gas; + uint256 nonce; + address to; + uint256 txType; + uint256 value; + } + + // Data structures to parse Transaction objects from the broadcast artifact + // that DO NOT conform to EIP1559. The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct TxLegacy { + string[] arguments; + address contractAddress; + string contractName; + string functionSig; + string hash; + string opcode; + TxDetailLegacy transaction; + } + + struct TxDetailLegacy { + AccessList[] accessList; + uint256 chainId; + bytes data; + address from; + uint256 gas; + uint256 gasPrice; + bytes32 hash; + uint256 nonce; + bytes1 opcode; + bytes32 r; + bytes32 s; + uint256 txType; + address to; + uint8 v; + uint256 value; + } + + struct AccessList { + address accessAddress; + bytes32[] storageKeys; + } + + // Data structures to parse Receipt objects from the broadcast artifact. + // The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct RawReceipt { + bytes32 blockHash; + bytes blockNumber; + address contractAddress; + bytes cumulativeGasUsed; + bytes effectiveGasPrice; + address from; + bytes gasUsed; + RawReceiptLog[] logs; + bytes logsBloom; + bytes status; + address to; + bytes32 transactionHash; + bytes transactionIndex; + } + + struct Receipt { + bytes32 blockHash; + uint256 blockNumber; + address contractAddress; + uint256 cumulativeGasUsed; + uint256 effectiveGasPrice; + address from; + uint256 gasUsed; + ReceiptLog[] logs; + bytes logsBloom; + uint256 status; + address to; + bytes32 transactionHash; + uint256 transactionIndex; + } + + // Data structures to parse the entire broadcast artifact, assuming the + // transactions conform to EIP1559. + + struct EIP1559ScriptArtifact { + string[] libraries; + string path; + string[] pending; + Receipt[] receipts; + uint256 timestamp; + Tx1559[] transactions; + TxReturn[] txReturns; + } + + struct RawEIP1559ScriptArtifact { + string[] libraries; + string path; + string[] pending; + RawReceipt[] receipts; + TxReturn[] txReturns; + uint256 timestamp; + RawTx1559[] transactions; + } + + struct RawReceiptLog { + // json value = address + address logAddress; + bytes32 blockHash; + bytes blockNumber; + bytes data; + bytes logIndex; + bool removed; + bytes32[] topics; + bytes32 transactionHash; + bytes transactionIndex; + bytes transactionLogIndex; + } + + struct ReceiptLog { + // json value = address + address logAddress; + bytes32 blockHash; + uint256 blockNumber; + bytes data; + uint256 logIndex; + bytes32[] topics; + uint256 transactionIndex; + uint256 transactionLogIndex; + bool removed; + } + + struct TxReturn { + string internalType; + string value; + } + + function assumeNoPrecompiles(address addr) internal virtual { + // Assembly required since `block.chainid` was introduced in 0.8.0. + uint256 chainId; + assembly { + chainId := chainid() + } + assumeNoPrecompiles(addr, chainId); + } + + function assumeNoPrecompiles(address addr, uint256 chainId) internal virtual { + // Note: For some chains like Optimism these are technically predeploys (i.e. bytecode placed at a specific + // address), but the same rationale for excluding them applies so we include those too. + + // These should be present on all EVM-compatible chains. + vm.assume(addr < address(0x1) || addr > address(0x9)); + + // forgefmt: disable-start + if (chainId == 10 || chainId == 420) { + // https://github.com/ethereum-optimism/optimism/blob/eaa371a0184b56b7ca6d9eb9cb0a2b78b2ccd864/op-bindings/predeploys/addresses.go#L6-L21 + vm.assume(addr < address(0x4200000000000000000000000000000000000000) || addr > address(0x4200000000000000000000000000000000000800)); + } else if (chainId == 42161 || chainId == 421613) { + // https://developer.arbitrum.io/useful-addresses#arbitrum-precompiles-l2-same-on-all-arb-chains + vm.assume(addr < address(0x0000000000000000000000000000000000000064) || addr > address(0x0000000000000000000000000000000000000068)); + } else if (chainId == 43114 || chainId == 43113) { + // https://github.com/ava-labs/subnet-evm/blob/47c03fd007ecaa6de2c52ea081596e0a88401f58/precompile/params.go#L18-L59 + vm.assume(addr < address(0x0100000000000000000000000000000000000000) || addr > address(0x01000000000000000000000000000000000000ff)); + vm.assume(addr < address(0x0200000000000000000000000000000000000000) || addr > address(0x02000000000000000000000000000000000000FF)); + vm.assume(addr < address(0x0300000000000000000000000000000000000000) || addr > address(0x03000000000000000000000000000000000000Ff)); + } + // forgefmt: disable-end + } + + function readEIP1559ScriptArtifact(string memory path) + internal + view + virtual + returns (EIP1559ScriptArtifact memory) + { + string memory data = vm.readFile(path); + bytes memory parsedData = vm.parseJson(data); + RawEIP1559ScriptArtifact memory rawArtifact = abi.decode(parsedData, (RawEIP1559ScriptArtifact)); + EIP1559ScriptArtifact memory artifact; + artifact.libraries = rawArtifact.libraries; + artifact.path = rawArtifact.path; + artifact.timestamp = rawArtifact.timestamp; + artifact.pending = rawArtifact.pending; + artifact.txReturns = rawArtifact.txReturns; + artifact.receipts = rawToConvertedReceipts(rawArtifact.receipts); + artifact.transactions = rawToConvertedEIPTx1559s(rawArtifact.transactions); + return artifact; + } + + function rawToConvertedEIPTx1559s(RawTx1559[] memory rawTxs) internal pure virtual returns (Tx1559[] memory) { + Tx1559[] memory txs = new Tx1559[](rawTxs.length); + for (uint256 i; i < rawTxs.length; i++) { + txs[i] = rawToConvertedEIPTx1559(rawTxs[i]); + } + return txs; + } + + function rawToConvertedEIPTx1559(RawTx1559 memory rawTx) internal pure virtual returns (Tx1559 memory) { + Tx1559 memory transaction; + transaction.arguments = rawTx.arguments; + transaction.contractName = rawTx.contractName; + transaction.functionSig = rawTx.functionSig; + transaction.hash = rawTx.hash; + transaction.txDetail = rawToConvertedEIP1559Detail(rawTx.txDetail); + transaction.opcode = rawTx.opcode; + return transaction; + } + + function rawToConvertedEIP1559Detail(RawTx1559Detail memory rawDetail) + internal + pure + virtual + returns (Tx1559Detail memory) + { + Tx1559Detail memory txDetail; + txDetail.data = rawDetail.data; + txDetail.from = rawDetail.from; + txDetail.to = rawDetail.to; + txDetail.nonce = _bytesToUint(rawDetail.nonce); + txDetail.txType = _bytesToUint(rawDetail.txType); + txDetail.value = _bytesToUint(rawDetail.value); + txDetail.gas = _bytesToUint(rawDetail.gas); + txDetail.accessList = rawDetail.accessList; + return txDetail; + } + + function readTx1559s(string memory path) internal view virtual returns (Tx1559[] memory) { + string memory deployData = vm.readFile(path); + bytes memory parsedDeployData = vm.parseJson(deployData, ".transactions"); + RawTx1559[] memory rawTxs = abi.decode(parsedDeployData, (RawTx1559[])); + return rawToConvertedEIPTx1559s(rawTxs); + } + + function readTx1559(string memory path, uint256 index) internal view virtual returns (Tx1559 memory) { + string memory deployData = vm.readFile(path); + string memory key = string(abi.encodePacked(".transactions[", vm.toString(index), "]")); + bytes memory parsedDeployData = vm.parseJson(deployData, key); + RawTx1559 memory rawTx = abi.decode(parsedDeployData, (RawTx1559)); + return rawToConvertedEIPTx1559(rawTx); + } + + // Analogous to readTransactions, but for receipts. + function readReceipts(string memory path) internal view virtual returns (Receipt[] memory) { + string memory deployData = vm.readFile(path); + bytes memory parsedDeployData = vm.parseJson(deployData, ".receipts"); + RawReceipt[] memory rawReceipts = abi.decode(parsedDeployData, (RawReceipt[])); + return rawToConvertedReceipts(rawReceipts); + } + + function readReceipt(string memory path, uint256 index) internal view virtual returns (Receipt memory) { + string memory deployData = vm.readFile(path); + string memory key = string(abi.encodePacked(".receipts[", vm.toString(index), "]")); + bytes memory parsedDeployData = vm.parseJson(deployData, key); + RawReceipt memory rawReceipt = abi.decode(parsedDeployData, (RawReceipt)); + return rawToConvertedReceipt(rawReceipt); + } + + function rawToConvertedReceipts(RawReceipt[] memory rawReceipts) internal pure virtual returns (Receipt[] memory) { + Receipt[] memory receipts = new Receipt[](rawReceipts.length); + for (uint256 i; i < rawReceipts.length; i++) { + receipts[i] = rawToConvertedReceipt(rawReceipts[i]); + } + return receipts; + } + + function rawToConvertedReceipt(RawReceipt memory rawReceipt) internal pure virtual returns (Receipt memory) { + Receipt memory receipt; + receipt.blockHash = rawReceipt.blockHash; + receipt.to = rawReceipt.to; + receipt.from = rawReceipt.from; + receipt.contractAddress = rawReceipt.contractAddress; + receipt.effectiveGasPrice = _bytesToUint(rawReceipt.effectiveGasPrice); + receipt.cumulativeGasUsed = _bytesToUint(rawReceipt.cumulativeGasUsed); + receipt.gasUsed = _bytesToUint(rawReceipt.gasUsed); + receipt.status = _bytesToUint(rawReceipt.status); + receipt.transactionIndex = _bytesToUint(rawReceipt.transactionIndex); + receipt.blockNumber = _bytesToUint(rawReceipt.blockNumber); + receipt.logs = rawToConvertedReceiptLogs(rawReceipt.logs); + receipt.logsBloom = rawReceipt.logsBloom; + receipt.transactionHash = rawReceipt.transactionHash; + return receipt; + } + + function rawToConvertedReceiptLogs(RawReceiptLog[] memory rawLogs) + internal + pure + virtual + returns (ReceiptLog[] memory) + { + ReceiptLog[] memory logs = new ReceiptLog[](rawLogs.length); + for (uint256 i; i < rawLogs.length; i++) { + logs[i].logAddress = rawLogs[i].logAddress; + logs[i].blockHash = rawLogs[i].blockHash; + logs[i].blockNumber = _bytesToUint(rawLogs[i].blockNumber); + logs[i].data = rawLogs[i].data; + logs[i].logIndex = _bytesToUint(rawLogs[i].logIndex); + logs[i].topics = rawLogs[i].topics; + logs[i].transactionIndex = _bytesToUint(rawLogs[i].transactionIndex); + logs[i].transactionLogIndex = _bytesToUint(rawLogs[i].transactionLogIndex); + logs[i].removed = rawLogs[i].removed; + } + return logs; + } + + // Deploy a contract by fetching the contract bytecode from + // the artifacts directory + // e.g. `deployCode(code, abi.encode(arg1,arg2,arg3))` + function deployCode(string memory what, bytes memory args) internal virtual returns (address addr) { + bytes memory bytecode = abi.encodePacked(vm.getCode(what), args); + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } + + require(addr != address(0), "StdCheats deployCode(string,bytes): Deployment failed."); + } + + function deployCode(string memory what) internal virtual returns (address addr) { + bytes memory bytecode = vm.getCode(what); + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } + + require(addr != address(0), "StdCheats deployCode(string): Deployment failed."); + } + + /// @dev deploy contract with value on construction + function deployCode(string memory what, bytes memory args, uint256 val) internal virtual returns (address addr) { + bytes memory bytecode = abi.encodePacked(vm.getCode(what), args); + /// @solidity memory-safe-assembly + assembly { + addr := create(val, add(bytecode, 0x20), mload(bytecode)) + } + + require(addr != address(0), "StdCheats deployCode(string,bytes,uint256): Deployment failed."); + } + + function deployCode(string memory what, uint256 val) internal virtual returns (address addr) { + bytes memory bytecode = vm.getCode(what); + /// @solidity memory-safe-assembly + assembly { + addr := create(val, add(bytecode, 0x20), mload(bytecode)) + } + + require(addr != address(0), "StdCheats deployCode(string,uint256): Deployment failed."); + } + + // creates a labeled address and the corresponding private key + function makeAddrAndKey(string memory name) internal virtual returns (address addr, uint256 privateKey) { + privateKey = uint256(keccak256(abi.encodePacked(name))); + addr = vm.addr(privateKey); + vm.label(addr, name); + } + + // creates a labeled address + function makeAddr(string memory name) internal virtual returns (address addr) { + (addr,) = makeAddrAndKey(name); + } + + function deriveRememberKey(string memory mnemonic, uint32 index) + internal + virtual + returns (address who, uint256 privateKey) + { + privateKey = vm.deriveKey(mnemonic, index); + who = vm.rememberKey(privateKey); + } + + function _bytesToUint(bytes memory b) private pure returns (uint256) { + require(b.length <= 32, "StdCheats _bytesToUint(bytes): Bytes length exceeds 32."); + return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); + } + + function isFork() internal virtual returns (bool status) { + try vm.activeFork() { + status = true; + } catch (bytes memory) {} + } + + modifier skipWhenForking() { + if (!isFork()) { + _; + } + } + + modifier skipWhenNotForking() { + if (isFork()) { + _; + } + } + + modifier noGasMetering() { + vm.pauseGasMetering(); + // To prevent turning gas monitoring back on with nested functions that use this modifier, + // we check if gasMetering started in the off position. If it did, we don't want to turn + // it back on until we exit the top level function that used the modifier + // + // i.e. funcA() noGasMetering { funcB() }, where funcB has noGasMetering as well. + // funcA will have `gasStartedOff` as false, funcB will have it as true, + // so we only turn metering back on at the end of the funcA + bool gasStartedOff = gasMeteringOff; + gasMeteringOff = true; + + _; + + // if gas metering was on when this modifier was called, turn it back on at the end + if (!gasStartedOff) { + gasMeteringOff = false; + vm.resumeGasMetering(); + } + } +} + +// Wrappers around cheatcodes to avoid footguns +abstract contract StdCheats is StdCheatsSafe { + using stdStorage for StdStorage; + + StdStorage private stdstore; + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + // Skip forward or rewind time by the specified number of seconds + function skip(uint256 time) internal virtual { + vm.warp(block.timestamp + time); + } + + function rewind(uint256 time) internal virtual { + vm.warp(block.timestamp - time); + } + + // Setup a prank from an address that has some ether + function hoax(address who) internal virtual { + vm.deal(who, 1 << 128); + vm.prank(who); + } + + function hoax(address who, uint256 give) internal virtual { + vm.deal(who, give); + vm.prank(who); + } + + function hoax(address who, address origin) internal virtual { + vm.deal(who, 1 << 128); + vm.prank(who, origin); + } + + function hoax(address who, address origin, uint256 give) internal virtual { + vm.deal(who, give); + vm.prank(who, origin); + } + + // Start perpetual prank from an address that has some ether + function startHoax(address who) internal virtual { + vm.deal(who, 1 << 128); + vm.startPrank(who); + } + + function startHoax(address who, uint256 give) internal virtual { + vm.deal(who, give); + vm.startPrank(who); + } + + // Start perpetual prank from an address that has some ether + // tx.origin is set to the origin parameter + function startHoax(address who, address origin) internal virtual { + vm.deal(who, 1 << 128); + vm.startPrank(who, origin); + } + + function startHoax(address who, address origin, uint256 give) internal virtual { + vm.deal(who, give); + vm.startPrank(who, origin); + } + + function changePrank(address who) internal virtual { + vm.stopPrank(); + vm.startPrank(who); + } + + // The same as Vm's `deal` + // Use the alternative signature for ERC20 tokens + function deal(address to, uint256 give) internal virtual { + vm.deal(to, give); + } + + // Set the balance of an account for any ERC20 token + // Use the alternative signature to update `totalSupply` + function deal(address token, address to, uint256 give) internal virtual { + deal(token, to, give, false); + } + + function deal(address token, address to, uint256 give, bool adjust) internal virtual { + // get current balance + (, bytes memory balData) = token.call(abi.encodeWithSelector(0x70a08231, to)); + uint256 prevBal = abi.decode(balData, (uint256)); + + // update balance + stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(give); + + // update total supply + if (adjust) { + (, bytes memory totSupData) = token.call(abi.encodeWithSelector(0x18160ddd)); + uint256 totSup = abi.decode(totSupData, (uint256)); + if (give < prevBal) { + totSup -= (prevBal - give); + } else { + totSup += (give - prevBal); + } + stdstore.target(token).sig(0x18160ddd).checked_write(totSup); + } + } +} diff --git a/lib/forge-std/src/StdError.sol b/lib/forge-std/src/StdError.sol new file mode 100644 index 0000000..a302191 --- /dev/null +++ b/lib/forge-std/src/StdError.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +// Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test +pragma solidity >=0.6.2 <0.9.0; + +library stdError { + bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); + bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); + bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); + bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); + bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); + bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); + bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); + bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); + bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); +} diff --git a/lib/forge-std/src/StdJson.sol b/lib/forge-std/src/StdJson.sol new file mode 100644 index 0000000..2dee471 --- /dev/null +++ b/lib/forge-std/src/StdJson.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; + +pragma experimental ABIEncoderV2; + +import {VmSafe} from "./Vm.sol"; + +// Helpers for parsing and writing JSON files +// To parse: +// ``` +// using stdJson for string; +// string memory json = vm.readFile("some_peth"); +// json.parseUint(""); +// ``` +// To write: +// ``` +// using stdJson for string; +// string memory json = "deploymentArtifact"; +// Contract contract = new Contract(); +// json.serialize("contractAddress", address(contract)); +// json = json.serialize("deploymentTimes", uint(1)); +// // store the stringified JSON to the 'json' variable we have been using as a key +// // as we won't need it any longer +// string memory json2 = "finalArtifact"; +// string memory final = json2.serialize("depArtifact", json); +// final.write(""); +// ``` + +library stdJson { + VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function parseRaw(string memory json, string memory key) internal pure returns (bytes memory) { + return vm.parseJson(json, key); + } + + function readUint(string memory json, string memory key) internal pure returns (uint256) { + return abi.decode(vm.parseJson(json, key), (uint256)); + } + + function readUintArray(string memory json, string memory key) internal pure returns (uint256[] memory) { + return abi.decode(vm.parseJson(json, key), (uint256[])); + } + + function readInt(string memory json, string memory key) internal pure returns (int256) { + return abi.decode(vm.parseJson(json, key), (int256)); + } + + function readIntArray(string memory json, string memory key) internal pure returns (int256[] memory) { + return abi.decode(vm.parseJson(json, key), (int256[])); + } + + function readBytes32(string memory json, string memory key) internal pure returns (bytes32) { + return abi.decode(vm.parseJson(json, key), (bytes32)); + } + + function readBytes32Array(string memory json, string memory key) internal pure returns (bytes32[] memory) { + return abi.decode(vm.parseJson(json, key), (bytes32[])); + } + + function readString(string memory json, string memory key) internal pure returns (string memory) { + return abi.decode(vm.parseJson(json, key), (string)); + } + + function readStringArray(string memory json, string memory key) internal pure returns (string[] memory) { + return abi.decode(vm.parseJson(json, key), (string[])); + } + + function readAddress(string memory json, string memory key) internal pure returns (address) { + return abi.decode(vm.parseJson(json, key), (address)); + } + + function readAddressArray(string memory json, string memory key) internal pure returns (address[] memory) { + return abi.decode(vm.parseJson(json, key), (address[])); + } + + function readBool(string memory json, string memory key) internal pure returns (bool) { + return abi.decode(vm.parseJson(json, key), (bool)); + } + + function readBoolArray(string memory json, string memory key) internal pure returns (bool[] memory) { + return abi.decode(vm.parseJson(json, key), (bool[])); + } + + function readBytes(string memory json, string memory key) internal pure returns (bytes memory) { + return abi.decode(vm.parseJson(json, key), (bytes)); + } + + function readBytesArray(string memory json, string memory key) internal pure returns (bytes[] memory) { + return abi.decode(vm.parseJson(json, key), (bytes[])); + } + + function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) { + return vm.serializeBool(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, bool[] memory value) + internal + returns (string memory) + { + return vm.serializeBool(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) { + return vm.serializeUint(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, uint256[] memory value) + internal + returns (string memory) + { + return vm.serializeUint(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) { + return vm.serializeInt(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, int256[] memory value) + internal + returns (string memory) + { + return vm.serializeInt(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) { + return vm.serializeAddress(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, address[] memory value) + internal + returns (string memory) + { + return vm.serializeAddress(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) { + return vm.serializeBytes32(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, bytes32[] memory value) + internal + returns (string memory) + { + return vm.serializeBytes32(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) { + return vm.serializeBytes(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, bytes[] memory value) + internal + returns (string memory) + { + return vm.serializeBytes(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, string memory value) + internal + returns (string memory) + { + return vm.serializeString(jsonKey, key, value); + } + + function serialize(string memory jsonKey, string memory key, string[] memory value) + internal + returns (string memory) + { + return vm.serializeString(jsonKey, key, value); + } + + function write(string memory jsonKey, string memory path) internal { + vm.writeJson(jsonKey, path); + } + + function write(string memory jsonKey, string memory path, string memory valueKey) internal { + vm.writeJson(jsonKey, path, valueKey); + } +} diff --git a/lib/forge-std/src/StdMath.sol b/lib/forge-std/src/StdMath.sol new file mode 100644 index 0000000..459523b --- /dev/null +++ b/lib/forge-std/src/StdMath.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +library stdMath { + int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; + + function abs(int256 a) internal pure returns (uint256) { + // Required or it will fail when `a = type(int256).min` + if (a == INT256_MIN) { + return 57896044618658097711785492504343953926634992332820282019728792003956564819968; + } + + return uint256(a > 0 ? a : -a); + } + + function delta(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b ? a - b : b - a; + } + + function delta(int256 a, int256 b) internal pure returns (uint256) { + // a and b are of the same sign + // this works thanks to two's complement, the left-most bit is the sign bit + if ((a ^ b) > -1) { + return delta(abs(a), abs(b)); + } + + // a and b are of opposite signs + return abs(a) + abs(b); + } + + function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 absDelta = delta(a, b); + + return absDelta * 1e18 / b; + } + + function percentDelta(int256 a, int256 b) internal pure returns (uint256) { + uint256 absDelta = delta(a, b); + uint256 absB = abs(b); + + return absDelta * 1e18 / absB; + } +} diff --git a/lib/forge-std/src/StdStorage.sol b/lib/forge-std/src/StdStorage.sol new file mode 100644 index 0000000..b0bfc00 --- /dev/null +++ b/lib/forge-std/src/StdStorage.sol @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +import {Vm} from "./Vm.sol"; + +struct StdStorage { + mapping(address => mapping(bytes4 => mapping(bytes32 => uint256))) slots; + mapping(address => mapping(bytes4 => mapping(bytes32 => bool))) finds; + bytes32[] _keys; + bytes4 _sig; + uint256 _depth; + address _target; + bytes32 _set; +} + +library stdStorageSafe { + event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint256 slot); + event WARNING_UninitedSlot(address who, uint256 slot); + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function sigs(string memory sigStr) internal pure returns (bytes4) { + return bytes4(keccak256(bytes(sigStr))); + } + + /// @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(StdStorage storage self) internal returns (uint256) { + address who = self._target; + bytes4 fsig = self._sig; + uint256 field_depth = self._depth; + bytes32[] memory ins = self._keys; + + // calldata to test against + if (self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { + return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; + } + bytes memory cald = abi.encodePacked(fsig, flatten(ins)); + vm.record(); + bytes32 fdat; + { + (, bytes memory rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32 * field_depth); + } + + (bytes32[] memory reads,) = vm.accesses(address(who)); + if (reads.length == 1) { + bytes32 curr = vm.load(who, reads[0]); + if (curr == bytes32(0)) { + emit WARNING_UninitedSlot(who, uint256(reads[0])); + } + if (fdat != curr) { + require( + false, + "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported." + ); + } + emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[0])); + self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[0]); + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; + } else if (reads.length > 1) { + for (uint256 i = 0; i < reads.length; i++) { + bytes32 prev = vm.load(who, reads[i]); + if (prev == bytes32(0)) { + emit WARNING_UninitedSlot(who, uint256(reads[i])); + } + // store + vm.store(who, reads[i], bytes32(hex"1337")); + bool success; + bytes memory rdat; + { + (success, rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32 * field_depth); + } + + if (success && fdat == bytes32(hex"1337")) { + // we found which of the slots is the actual one + emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[i])); + self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[i]); + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; + vm.store(who, reads[i], prev); + break; + } + vm.store(who, reads[i], prev); + } + } else { + require(false, "stdStorage find(StdStorage): No storage use detected for target."); + } + + require( + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))], + "stdStorage find(StdStorage): Slot(s) not found." + ); + + delete self._target; + delete self._sig; + delete self._keys; + delete self._depth; + + return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; + } + + function target(StdStorage storage self, address _target) internal returns (StdStorage storage) { + self._target = _target; + return self; + } + + function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) { + self._sig = _sig; + return self; + } + + function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) { + self._sig = sigs(_sig); + return self; + } + + function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) { + self._keys.push(bytes32(uint256(uint160(who)))); + return self; + } + + function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) { + self._keys.push(bytes32(amt)); + return self; + } + + function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) { + self._keys.push(key); + return self; + } + + function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) { + self._depth = _depth; + return self; + } + + function read(StdStorage storage self) private returns (bytes memory) { + address t = self._target; + uint256 s = find(self); + return abi.encode(vm.load(t, bytes32(s))); + } + + function read_bytes32(StdStorage storage self) internal returns (bytes32) { + return abi.decode(read(self), (bytes32)); + } + + function read_bool(StdStorage storage self) internal returns (bool) { + int256 v = read_int(self); + if (v == 0) return false; + if (v == 1) return true; + revert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); + } + + function read_address(StdStorage storage self) internal returns (address) { + return abi.decode(read(self), (address)); + } + + function read_uint(StdStorage storage self) internal returns (uint256) { + return abi.decode(read(self), (uint256)); + } + + function read_int(StdStorage storage self) internal returns (int256) { + return abi.decode(read(self), (int256)); + } + + function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) { + bytes32 out; + + uint256 max = b.length > 32 ? 32 : b.length; + for (uint256 i = 0; i < max; i++) { + out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); + } + return out; + } + + function flatten(bytes32[] memory b) private pure returns (bytes memory) { + bytes memory result = new bytes(b.length * 32); + for (uint256 i = 0; i < b.length; i++) { + bytes32 k = b[i]; + /// @solidity memory-safe-assembly + assembly { + mstore(add(result, add(32, mul(32, i))), k) + } + } + + return result; + } +} + +library stdStorage { + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function sigs(string memory sigStr) internal pure returns (bytes4) { + return stdStorageSafe.sigs(sigStr); + } + + function find(StdStorage storage self) internal returns (uint256) { + return stdStorageSafe.find(self); + } + + function target(StdStorage storage self, address _target) internal returns (StdStorage storage) { + return stdStorageSafe.target(self, _target); + } + + function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) { + return stdStorageSafe.sig(self, _sig); + } + + function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) { + return stdStorageSafe.sig(self, _sig); + } + + function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) { + return stdStorageSafe.with_key(self, who); + } + + function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) { + return stdStorageSafe.with_key(self, amt); + } + + function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) { + return stdStorageSafe.with_key(self, key); + } + + function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) { + return stdStorageSafe.depth(self, _depth); + } + + function checked_write(StdStorage storage self, address who) internal { + checked_write(self, bytes32(uint256(uint160(who)))); + } + + function checked_write(StdStorage storage self, uint256 amt) internal { + checked_write(self, bytes32(amt)); + } + + function checked_write(StdStorage storage self, bool write) internal { + bytes32 t; + /// @solidity memory-safe-assembly + assembly { + t := write + } + checked_write(self, t); + } + + function checked_write(StdStorage storage self, bytes32 set) internal { + address who = self._target; + bytes4 fsig = self._sig; + uint256 field_depth = self._depth; + bytes32[] memory ins = self._keys; + + bytes memory cald = abi.encodePacked(fsig, flatten(ins)); + if (!self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { + find(self); + } + bytes32 slot = bytes32(self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]); + + bytes32 fdat; + { + (, bytes memory rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32 * field_depth); + } + bytes32 curr = vm.load(who, slot); + + if (fdat != curr) { + require( + false, + "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported." + ); + } + vm.store(who, slot, set); + delete self._target; + delete self._sig; + delete self._keys; + delete self._depth; + } + + function read_bytes32(StdStorage storage self) internal returns (bytes32) { + return stdStorageSafe.read_bytes32(self); + } + + function read_bool(StdStorage storage self) internal returns (bool) { + return stdStorageSafe.read_bool(self); + } + + function read_address(StdStorage storage self) internal returns (address) { + return stdStorageSafe.read_address(self); + } + + function read_uint(StdStorage storage self) internal returns (uint256) { + return stdStorageSafe.read_uint(self); + } + + function read_int(StdStorage storage self) internal returns (int256) { + return stdStorageSafe.read_int(self); + } + + // Private function so needs to be copied over + function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) { + bytes32 out; + + uint256 max = b.length > 32 ? 32 : b.length; + for (uint256 i = 0; i < max; i++) { + out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); + } + return out; + } + + // Private function so needs to be copied over + function flatten(bytes32[] memory b) private pure returns (bytes memory) { + bytes memory result = new bytes(b.length * 32); + for (uint256 i = 0; i < b.length; i++) { + bytes32 k = b[i]; + /// @solidity memory-safe-assembly + assembly { + mstore(add(result, add(32, mul(32, i))), k) + } + } + + return result; + } +} diff --git a/lib/forge-std/src/StdUtils.sol b/lib/forge-std/src/StdUtils.sol new file mode 100644 index 0000000..a283e75 --- /dev/null +++ b/lib/forge-std/src/StdUtils.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +// TODO Remove import. +import {VmSafe} from "./Vm.sol"; + +abstract contract StdUtils { + VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); + address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67; + + uint256 private constant INT256_MIN_ABS = + 57896044618658097711785492504343953926634992332820282019728792003956564819968; + uint256 private constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; + + function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) { + require(min <= max, "StdUtils bound(uint256,uint256,uint256): Max is less than min."); + // If x is between min and max, return x directly. This is to ensure that dictionary values + // do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188 + if (x >= min && x <= max) return x; + + uint256 size = max - min + 1; + + // If the value is 0, 1, 2, 3, warp that to min, min+1, min+2, min+3. Similarly for the UINT256_MAX side. + // This helps ensure coverage of the min/max values. + if (x <= 3 && size > x) return min + x; + if (x >= UINT256_MAX - 3 && size > UINT256_MAX - x) return max - (UINT256_MAX - x); + + // Otherwise, wrap x into the range [min, max], i.e. the range is inclusive. + if (x > max) { + uint256 diff = x - max; + uint256 rem = diff % size; + if (rem == 0) return max; + result = min + rem - 1; + } else if (x < min) { + uint256 diff = min - x; + uint256 rem = diff % size; + if (rem == 0) return min; + result = max - rem + 1; + } + } + + function bound(uint256 x, uint256 min, uint256 max) internal view virtual returns (uint256 result) { + result = _bound(x, min, max); + console2_log("Bound Result", result); + } + + function bound(int256 x, int256 min, int256 max) internal view virtual returns (int256 result) { + require(min <= max, "StdUtils bound(int256,int256,int256): Max is less than min."); + + // Shifting all int256 values to uint256 to use _bound function. The range of two types are: + // int256 : -(2**255) ~ (2**255 - 1) + // uint256: 0 ~ (2**256 - 1) + // So, add 2**255, INT256_MIN_ABS to the integer values. + // + // If the given integer value is -2**255, we cannot use `-uint256(-x)` because of the overflow. + // So, use `~uint256(x) + 1` instead. + uint256 _x = x < 0 ? (INT256_MIN_ABS - ~uint256(x) - 1) : (uint256(x) + INT256_MIN_ABS); + uint256 _min = min < 0 ? (INT256_MIN_ABS - ~uint256(min) - 1) : (uint256(min) + INT256_MIN_ABS); + uint256 _max = max < 0 ? (INT256_MIN_ABS - ~uint256(max) - 1) : (uint256(max) + INT256_MIN_ABS); + + uint256 y = _bound(_x, _min, _max); + + // To move it back to int256 value, subtract INT256_MIN_ABS at here. + result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS); + console2_log("Bound result", vm.toString(result)); + } + + /// @dev Compute the address a contract will be deployed at for a given deployer address and nonce + /// @notice adapated from Solmate implementation (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol) + function computeCreateAddress(address deployer, uint256 nonce) internal pure virtual returns (address) { + // forgefmt: disable-start + // The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0. + // A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it. + if (nonce == 0x00) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80)))); + if (nonce <= 0x7f) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce)))); + + // Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length. + if (nonce <= 2**8 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce)))); + if (nonce <= 2**16 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce)))); + if (nonce <= 2**24 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce)))); + // forgefmt: disable-end + + // More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp + // 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + // 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex) + // We assume nobody can have a nonce large enough to require more than 32 bytes. + return addressFromLast20Bytes( + keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))) + ); + } + + function computeCreate2Address(bytes32 salt, bytes32 initcodeHash, address deployer) + internal + pure + virtual + returns (address) + { + return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initcodeHash))); + } + + function bytesToUint(bytes memory b) internal pure virtual returns (uint256) { + require(b.length <= 32, "StdUtils bytesToUint(bytes): Bytes length exceeds 32."); + return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256)); + } + + function addressFromLast20Bytes(bytes32 bytesValue) private pure returns (address) { + return address(uint160(uint256(bytesValue))); + } + + // Used to prevent the compilation of console, which shortens the compilation time when console is not used elsewhere. + + function console2_log(string memory p0, uint256 p1) private view { + (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + status; + } + + function console2_log(string memory p0, string memory p1) private view { + (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,string)", p0, p1)); + status; + } +} diff --git a/lib/forge-std/src/Test.sol b/lib/forge-std/src/Test.sol new file mode 100644 index 0000000..6c26230 --- /dev/null +++ b/lib/forge-std/src/Test.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +// 💬 ABOUT +// Standard Library's default Test + +// 🧩 MODULES +import {console} from "./console.sol"; +import {console2} from "./console2.sol"; +import {StdAssertions} from "./StdAssertions.sol"; +import {StdChains} from "./StdChains.sol"; +import {StdCheats} from "./StdCheats.sol"; +import {stdError} from "./StdError.sol"; +import {stdJson} from "./StdJson.sol"; +import {stdMath} from "./StdMath.sol"; +import {StdStorage, stdStorage} from "./StdStorage.sol"; +import {StdUtils} from "./StdUtils.sol"; +import {Vm} from "./Vm.sol"; + +// 📦 BOILERPLATE +import {TestBase} from "./Base.sol"; +import {DSTest} from "ds-test/test.sol"; + +// ⭐️ TEST +abstract contract Test is DSTest, StdAssertions, StdChains, StdCheats, StdUtils, TestBase { +// Note: IS_TEST() must return true. +// Note: Must have failure system, https://github.com/dapphub/ds-test/blob/cd98eff28324bfac652e63a239a60632a761790b/src/test.sol#L39-L76. +} diff --git a/lib/forge-std/src/Vm.sol b/lib/forge-std/src/Vm.sol new file mode 100644 index 0000000..6ebde70 --- /dev/null +++ b/lib/forge-std/src/Vm.sol @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +// Cheatcodes are marked as view/pure/none using the following rules: +// 0. A call's observable behaviour includes its return value, logs, reverts and state writes, +// 1. If you can influence a later call's observable behaviour, you're neither `view` nor `pure (you are modifying some state be it the EVM, interpreter, filesystem, etc), +// 2. Otherwise if you can be influenced by an earlier call, or if reading some state, you're `view`, +// 3. Otherwise you're `pure`. + +interface VmSafe { + struct Log { + bytes32[] topics; + bytes data; + address emitter; + } + + struct Rpc { + string key; + string url; + } + + struct FsMetadata { + bool isDir; + bool isSymlink; + uint256 length; + bool readOnly; + uint256 modified; + uint256 accessed; + uint256 created; + } + + // Loads a storage slot from an address + function load(address target, bytes32 slot) external view returns (bytes32 data); + // Signs data + function sign(uint256 privateKey, bytes32 digest) external pure returns (uint8 v, bytes32 r, bytes32 s); + // Gets the address for a given private key + function addr(uint256 privateKey) external pure returns (address addr); + // Gets the nonce of an account + function getNonce(address account) external view returns (uint64 nonce); + // Performs a foreign function call via the terminal + function ffi(string[] calldata commandInput) external returns (bytes memory result); + // Sets environment variables + function setEnv(string calldata name, string calldata value) external; + // Reads environment variables, (name) => (value) + function envBool(string calldata name) external view returns (bool value); + function envUint(string calldata name) external view returns (uint256 value); + function envInt(string calldata name) external view returns (int256 value); + function envAddress(string calldata name) external view returns (address value); + function envBytes32(string calldata name) external view returns (bytes32 value); + function envString(string calldata name) external view returns (string memory value); + function envBytes(string calldata name) external view returns (bytes memory value); + // Reads environment variables as arrays + function envBool(string calldata name, string calldata delim) external view returns (bool[] memory value); + function envUint(string calldata name, string calldata delim) external view returns (uint256[] memory value); + function envInt(string calldata name, string calldata delim) external view returns (int256[] memory value); + function envAddress(string calldata name, string calldata delim) external view returns (address[] memory value); + function envBytes32(string calldata name, string calldata delim) external view returns (bytes32[] memory value); + function envString(string calldata name, string calldata delim) external view returns (string[] memory value); + function envBytes(string calldata name, string calldata delim) external view returns (bytes[] memory value); + // Read environment variables with default value + function envOr(string calldata name, bool defaultValue) external returns (bool value); + function envOr(string calldata name, uint256 defaultValue) external returns (uint256 value); + function envOr(string calldata name, int256 defaultValue) external returns (int256 value); + function envOr(string calldata name, address defaultValue) external returns (address value); + function envOr(string calldata name, bytes32 defaultValue) external returns (bytes32 value); + function envOr(string calldata name, string calldata defaultValue) external returns (string memory value); + function envOr(string calldata name, bytes calldata defaultValue) external returns (bytes memory value); + // Read environment variables as arrays with default value + function envOr(string calldata name, string calldata delim, bool[] calldata defaultValue) + external + returns (bool[] memory value); + function envOr(string calldata name, string calldata delim, uint256[] calldata defaultValue) + external + returns (uint256[] memory value); + function envOr(string calldata name, string calldata delim, int256[] calldata defaultValue) + external + returns (int256[] memory value); + function envOr(string calldata name, string calldata delim, address[] calldata defaultValue) + external + returns (address[] memory value); + function envOr(string calldata name, string calldata delim, bytes32[] calldata defaultValue) + external + returns (bytes32[] memory value); + function envOr(string calldata name, string calldata delim, string[] calldata defaultValue) + external + returns (string[] memory value); + function envOr(string calldata name, string calldata delim, bytes[] calldata defaultValue) + external + returns (bytes[] memory value); + // Records all storage reads and writes + function record() external; + // Gets all accessed reads and write slot from a recording session, for a given address + function accesses(address target) external returns (bytes32[] memory readSlots, bytes32[] memory writeSlots); + // Gets the _creation_ bytecode from an artifact file. Takes in the relative path to the json file + function getCode(string calldata artifactPath) external view returns (bytes memory creationBytecode); + // Gets the _deployed_ bytecode from an artifact file. Takes in the relative path to the json file + function getDeployedCode(string calldata artifactPath) external view returns (bytes memory runtimeBytecode); + // Labels an address in call traces + function label(address account, string calldata newLabel) external; + // Using the address that calls the test contract, has the next call (at this call depth only) create a transaction that can later be signed and sent onchain + function broadcast() external; + // Has the next call (at this call depth only) create a transaction with the address provided as the sender that can later be signed and sent onchain + function broadcast(address signer) external; + // Has the next call (at this call depth only) create a transaction with the private key provided as the sender that can later be signed and sent onchain + function broadcast(uint256 privateKey) external; + // Using the address that calls the test contract, has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain + function startBroadcast() external; + // Has all subsequent calls (at this call depth only) create transactions with the address provided that can later be signed and sent onchain + function startBroadcast(address signer) external; + // Has all subsequent calls (at this call depth only) create transactions with the private key provided that can later be signed and sent onchain + function startBroadcast(uint256 privateKey) external; + // Stops collecting onchain transactions + function stopBroadcast() external; + // Reads the entire content of file to string + function readFile(string calldata path) external view returns (string memory data); + // Reads the entire content of file as binary. Path is relative to the project root. + function readFileBinary(string calldata path) external view returns (bytes memory data); + // Get the path of the current project root + function projectRoot() external view returns (string memory path); + // Get the metadata for a file/directory + function fsMetadata(string calldata fileOrDir) external returns (FsMetadata memory metadata); + // Reads next line of file to string + function readLine(string calldata path) external view returns (string memory line); + // Writes data to file, creating a file if it does not exist, and entirely replacing its contents if it does. + function writeFile(string calldata path, string calldata data) external; + // Writes binary data to a file, creating a file if it does not exist, and entirely replacing its contents if it does. + // Path is relative to the project root. + function writeFileBinary(string calldata path, bytes calldata data) external; + // Writes line to file, creating a file if it does not exist. + function writeLine(string calldata path, string calldata data) external; + // Closes file for reading, resetting the offset and allowing to read it from beginning with readLine. + function closeFile(string calldata path) external; + // Removes file. This cheatcode will revert in the following situations, but is not limited to just these cases: + // - Path points to a directory. + // - The file doesn't exist. + // - The user lacks permissions to remove the file. + function removeFile(string calldata path) external; + // Convert values to a string + function toString(address value) external pure returns (string memory stringifiedValue); + function toString(bytes calldata value) external pure returns (string memory stringifiedValue); + function toString(bytes32 value) external pure returns (string memory stringifiedValue); + function toString(bool value) external pure returns (string memory stringifiedValue); + function toString(uint256 value) external pure returns (string memory stringifiedValue); + function toString(int256 value) external pure returns (string memory stringifiedValue); + // Convert values from a string + function parseBytes(string calldata stringifiedValue) external pure returns (bytes memory parsedValue); + function parseAddress(string calldata stringifiedValue) external pure returns (address parsedValue); + function parseUint(string calldata stringifiedValue) external pure returns (uint256 parsedValue); + function parseInt(string calldata stringifiedValue) external pure returns (int256 parsedValue); + function parseBytes32(string calldata stringifiedValue) external pure returns (bytes32 parsedValue); + function parseBool(string calldata stringifiedValue) external pure returns (bool parsedValue); + // Record all the transaction logs + function recordLogs() external; + // Gets all the recorded logs + function getRecordedLogs() external returns (Log[] memory logs); + // Derive a private key from a provided mnenomic string (or mnenomic file path) at the derivation path m/44'/60'/0'/0/{index} + function deriveKey(string calldata mnemonic, uint32 index) external pure returns (uint256 privateKey); + // Derive a private key from a provided mnenomic string (or mnenomic file path) at {derivationPath}{index} + function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index) + external + pure + returns (uint256 privateKey); + // Adds a private key to the local forge wallet and returns the address + function rememberKey(uint256 privateKey) external returns (address addr); + // + // parseJson + // + // ---- + // In case the returned value is a JSON object, it's encoded as a ABI-encoded tuple. As JSON objects + // don't have the notion of ordered, but tuples do, they JSON object is encoded with it's fields ordered in + // ALPHABETICAL order. That means that in order to successfully decode the tuple, we need to define a tuple that + // encodes the fields in the same order, which is alphabetical. In the case of Solidity structs, they are encoded + // as tuples, with the attributes in the order in which they are defined. + // For example: json = { 'a': 1, 'b': 0xa4tb......3xs} + // a: uint256 + // b: address + // To decode that json, we need to define a struct or a tuple as follows: + // struct json = { uint256 a; address b; } + // If we defined a json struct with the opposite order, meaning placing the address b first, it would try to + // decode the tuple in that order, and thus fail. + // ---- + // Given a string of JSON, return it as ABI-encoded + function parseJson(string calldata json, string calldata key) external pure returns (bytes memory abiEncodedData); + function parseJson(string calldata json) external pure returns (bytes memory abiEncodedData); + + // Serialize a key and value to a JSON object stored in-memory that can be later written to a file + // It returns the stringified version of the specific JSON file up to that moment. + function serializeBool(string calldata objectKey, string calldata valueKey, bool value) + external + returns (string memory json); + function serializeUint(string calldata objectKey, string calldata valueKey, uint256 value) + external + returns (string memory json); + function serializeInt(string calldata objectKey, string calldata valueKey, int256 value) + external + returns (string memory json); + function serializeAddress(string calldata objectKey, string calldata valueKey, address value) + external + returns (string memory json); + function serializeBytes32(string calldata objectKey, string calldata valueKey, bytes32 value) + external + returns (string memory json); + function serializeString(string calldata objectKey, string calldata valueKey, string calldata value) + external + returns (string memory json); + function serializeBytes(string calldata objectKey, string calldata valueKey, bytes calldata value) + external + returns (string memory json); + + function serializeBool(string calldata objectKey, string calldata valueKey, bool[] calldata values) + external + returns (string memory json); + function serializeUint(string calldata objectKey, string calldata valueKey, uint256[] calldata values) + external + returns (string memory json); + function serializeInt(string calldata objectKey, string calldata valueKey, int256[] calldata values) + external + returns (string memory json); + function serializeAddress(string calldata objectKey, string calldata valueKey, address[] calldata values) + external + returns (string memory json); + function serializeBytes32(string calldata objectKey, string calldata valueKey, bytes32[] calldata values) + external + returns (string memory json); + function serializeString(string calldata objectKey, string calldata valueKey, string[] calldata values) + external + returns (string memory json); + function serializeBytes(string calldata objectKey, string calldata valueKey, bytes[] calldata values) + external + returns (string memory json); + + // + // writeJson + // + // ---- + // Write a serialized JSON object to a file. If the file exists, it will be overwritten. + // Let's assume we want to write the following JSON to a file: + // + // { "boolean": true, "number": 342, "object": { "title": "finally json serialization" } } + // + // ``` + // string memory json1 = "some key"; + // vm.serializeBool(json1, "boolean", true); + // vm.serializeBool(json1, "number", uint256(342)); + // json2 = "some other key"; + // string memory output = vm.serializeString(json2, "title", "finally json serialization"); + // string memory finalJson = vm.serialize(json1, "object", output); + // vm.writeJson(finalJson, "./output/example.json"); + // ``` + // The critical insight is that every invocation of serialization will return the stringified version of the JSON + // up to that point. That means we can construct arbitrary JSON objects and then use the return stringified version + // to serialize them as values to another JSON object. + // + // json1 and json2 are simply keys used by the backend to keep track of the objects. So vm.serializeJson(json1,..) + // will find the object in-memory that is keyed by "some key". + function writeJson(string calldata json, string calldata path) external; + // Write a serialized JSON object to an **existing** JSON file, replacing a value with key = + // This is useful to replace a specific value of a JSON file, without having to parse the entire thing + function writeJson(string calldata json, string calldata path, string calldata valueKey) external; + // Returns the RPC url for the given alias + function rpcUrl(string calldata rpcAlias) external view returns (string memory json); + // Returns all rpc urls and their aliases `[alias, url][]` + function rpcUrls() external view returns (string[2][] memory urls); + // Returns all rpc urls and their aliases as structs. + function rpcUrlStructs() external view returns (Rpc[] memory urls); + // If the condition is false, discard this run's fuzz inputs and generate new ones. + function assume(bool condition) external pure; + // Pauses gas metering (i.e. gas usage is not counted). Noop if already paused. + function pauseGasMetering() external; + // Resumes gas metering (i.e. gas usage is counted again). Noop if already on. + function resumeGasMetering() external; +} + +interface Vm is VmSafe { + // Sets block.timestamp + function warp(uint256 newTimestamp) external; + // Sets block.height + function roll(uint256 newHeight) external; + // Sets block.basefee + function fee(uint256 newBasefee) external; + // Sets block.difficulty + function difficulty(uint256 newDifficulty) external; + // Sets block.chainid + function chainId(uint256 newChainId) external; + // Stores a value to an address' storage slot. + function store(address target, bytes32 slot, bytes32 value) external; + // Sets the nonce of an account; must be higher than the current nonce of the account + function setNonce(address account, uint64 newNonce) external; + // Sets the *next* call's msg.sender to be the input address + function prank(address msgSender) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called + function startPrank(address msgSender) external; + // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input + function prank(address msgSender, address txOrigin) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input + function startPrank(address msgSender, address txOrigin) external; + // Resets subsequent calls' msg.sender to be `address(this)` + function stopPrank() external; + // Sets an address' balance + function deal(address account, uint256 newBalance) external; + // Sets an address' code + function etch(address target, bytes calldata newRuntimeBytecode) external; + // Expects an error on next call + function expectRevert(bytes calldata revertData) external; + function expectRevert(bytes4 revertData) external; + function expectRevert() external; + // Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). + // Call this function, then emit an event, then call a function. Internally after the call, we check if + // logs were emitted in the expected order with the expected topics and data (as specified by the booleans) + function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) external; + function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData, address emitter) + external; + // Mocks a call to an address, returning specified data. + // Calldata can either be strict or a partial match, e.g. if you only + // pass a Solidity selector to the expected calldata, then the entire Solidity + // function will be mocked. + function mockCall(address callee, bytes calldata data, bytes calldata returnData) external; + // Mocks a call to an address with a specific msg.value, returning specified data. + // Calldata match takes precedence over msg.value in case of ambiguity. + function mockCall(address callee, uint256 msgValue, bytes calldata data, bytes calldata returnData) external; + // Clears all mocked calls + function clearMockedCalls() external; + // Expects a call to an address with the specified calldata. + // Calldata can either be a strict or a partial match + function expectCall(address callee, bytes calldata data) external; + // Expects a call to an address with the specified msg.value and calldata + function expectCall(address callee, uint256 msgValue, bytes calldata data) external; + // Sets block.coinbase + function coinbase(address newCoinbase) external; + // Snapshot the current state of the evm. + // Returns the id of the snapshot that was created. + // To revert a snapshot use `revertTo` + function snapshot() external returns (uint256 snapshotId); + // Revert the state of the EVM to a previous snapshot + // Takes the snapshot id to revert to. + // This deletes the snapshot and all snapshots taken after the given snapshot id. + function revertTo(uint256 snapshotId) external returns (bool success); + // Creates a new fork with the given endpoint and block and returns the identifier of the fork + function createFork(string calldata urlOrAlias, uint256 blockNumber) external returns (uint256 forkId); + // Creates a new fork with the given endpoint and the _latest_ block and returns the identifier of the fork + function createFork(string calldata urlOrAlias) external returns (uint256 forkId); + // Creates a new fork with the given endpoint and at the block the given transaction was mined in, replays all transaction mined in the block before the transaction, + // and returns the identifier of the fork + function createFork(string calldata urlOrAlias, bytes32 txHash) external returns (uint256 forkId); + // Creates _and_ also selects a new fork with the given endpoint and block and returns the identifier of the fork + function createSelectFork(string calldata urlOrAlias, uint256 blockNumber) external returns (uint256 forkId); + // Creates _and_ also selects new fork with the given endpoint and at the block the given transaction was mined in, replays all transaction mined in the block before + // the transaction, returns the identifier of the fork + function createSelectFork(string calldata urlOrAlias, bytes32 txHash) external returns (uint256 forkId); + // Creates _and_ also selects a new fork with the given endpoint and the latest block and returns the identifier of the fork + function createSelectFork(string calldata urlOrAlias) external returns (uint256 forkId); + // Takes a fork identifier created by `createFork` and sets the corresponding forked state as active. + function selectFork(uint256 forkId) external; + /// Returns the identifier of the currently active fork. Reverts if no fork is currently active. + function activeFork() external view returns (uint256 forkId); + // Updates the currently active fork to given block number + // This is similar to `roll` but for the currently active fork + function rollFork(uint256 blockNumber) external; + // Updates the currently active fork to given transaction + // this will `rollFork` with the number of the block the transaction was mined in and replays all transaction mined before it in the block + function rollFork(bytes32 txHash) external; + // Updates the given fork to given block number + function rollFork(uint256 forkId, uint256 blockNumber) external; + // Updates the given fork to block number of the given transaction and replays all transaction mined before it in the block + function rollFork(uint256 forkId, bytes32 txHash) external; + // Marks that the account(s) should use persistent storage across fork swaps in a multifork setup + // Meaning, changes made to the state of this account will be kept when switching forks + function makePersistent(address account) external; + function makePersistent(address account0, address account1) external; + function makePersistent(address account0, address account1, address account2) external; + function makePersistent(address[] calldata accounts) external; + // Revokes persistent status from the address, previously added via `makePersistent` + function revokePersistent(address account) external; + function revokePersistent(address[] calldata accounts) external; + // Returns true if the account is marked as persistent + function isPersistent(address account) external view returns (bool persistent); + // In forking mode, explicitly grant the given address cheatcode access + function allowCheatcodes(address account) external; + // Fetches the given transaction from the active fork and executes it on the current state + function transact(bytes32 txHash) external; + // Fetches the given transaction from the given fork and executes it on the current state + function transact(uint256 forkId, bytes32 txHash) external; +} diff --git a/lib/forge-std/src/console.sol b/lib/forge-std/src/console.sol new file mode 100644 index 0000000..ad57e53 --- /dev/null +++ b/lib/forge-std/src/console.sol @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +library console { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + /// @solidity memory-safe-assembly + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); + } + + function logUint(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); + } + + function log(uint p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); + } + + function log(uint p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); + } + + function log(uint p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); + } + + function log(string memory p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); + } + + function log(uint p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); + } + + function log(uint p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); + } + + function log(uint p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); + } + + function log(uint p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); + } + + function log(uint p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); + } + + function log(uint p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); + } + + function log(uint p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); + } + + function log(uint p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); + } + + function log(uint p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); + } + + function log(uint p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); + } + + function log(uint p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); + } + + function log(bool p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); + } + + function log(bool p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); + } + + function log(bool p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); + } + + function log(address p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); + } + + function log(address p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); + } + + function log(address p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} \ No newline at end of file diff --git a/lib/forge-std/src/console2.sol b/lib/forge-std/src/console2.sol new file mode 100644 index 0000000..8596233 --- /dev/null +++ b/lib/forge-std/src/console2.sol @@ -0,0 +1,1546 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +/// @dev The original console.sol uses `int` and `uint` for computing function selectors, but it should +/// use `int256` and `uint256`. This modified version fixes that. This version is recommended +/// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in +/// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`. +/// Reference: https://github.com/NomicFoundation/hardhat/issues/2178 +library console2 { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + /// @solidity memory-safe-assembly + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); + } + + function logUint(uint256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); + } + + function log(int256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint256 p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); + } + + function log(uint256 p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); + } + + function log(uint256 p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); + } + + function log(uint256 p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); + } + + function log(string memory p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + } + + function log(string memory p0, int256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,int256)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint256 p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} \ No newline at end of file diff --git a/lib/forge-std/src/interfaces/IERC1155.sol b/lib/forge-std/src/interfaces/IERC1155.sol new file mode 100644 index 0000000..f7dd2b4 --- /dev/null +++ b/lib/forge-std/src/interfaces/IERC1155.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2; + +import "./IERC165.sol"; + +/// @title ERC-1155 Multi Token Standard +/// @dev See https://eips.ethereum.org/EIPS/eip-1155 +/// Note: The ERC-165 identifier for this interface is 0xd9b67a26. +interface IERC1155 is IERC165 { + /// @dev + /// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). + /// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). + /// - The `_from` argument MUST be the address of the holder whose balance is decreased. + /// - The `_to` argument MUST be the address of the recipient whose balance is increased. + /// - The `_id` argument MUST be the token type being transferred. + /// - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. + /// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). + /// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). + event TransferSingle( + address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value + ); + + /// @dev + /// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). + /// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). + /// - The `_from` argument MUST be the address of the holder whose balance is decreased. + /// - The `_to` argument MUST be the address of the recipient whose balance is increased. + /// - The `_ids` argument MUST be the list of tokens being transferred. + /// - The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by. + /// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). + /// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). + event TransferBatch( + address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values + ); + + /// @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled). + event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); + + /// @dev MUST emit when the URI is updated for a token ID. URIs are defined in RFC 3986. + /// The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". + event URI(string _value, uint256 indexed _id); + + /// @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call). + /// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). + /// - MUST revert if `_to` is the zero address. + /// - MUST revert if balance of holder for token `_id` is lower than the `_value` sent. + /// - MUST revert on any other error. + /// - MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard). + /// - After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). + /// @param _from Source address + /// @param _to Target address + /// @param _id ID of the token type + /// @param _value Transfer amount + /// @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to` + function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; + + /// @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call). + /// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). + /// - MUST revert if `_to` is the zero address. + /// - MUST revert if length of `_ids` is not the same as length of `_values`. + /// - MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. + /// - MUST revert on any other error. + /// - MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard). + /// - Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc). + /// - After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). + /// @param _from Source address + /// @param _to Target address + /// @param _ids IDs of each token type (order and length must match _values array) + /// @param _values Transfer amounts per token type (order and length must match _ids array) + /// @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to` + function safeBatchTransferFrom( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _values, + bytes calldata _data + ) external; + + /// @notice Get the balance of an account's tokens. + /// @param _owner The address of the token holder + /// @param _id ID of the token + /// @return The _owner's balance of the token type requested + function balanceOf(address _owner, uint256 _id) external view returns (uint256); + + /// @notice Get the balance of multiple account/token pairs + /// @param _owners The addresses of the token holders + /// @param _ids ID of the tokens + /// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair) + function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) + external + view + returns (uint256[] memory); + + /// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. + /// @dev MUST emit the ApprovalForAll event on success. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if the operator is approved, false to revoke approval + function setApprovalForAll(address _operator, bool _approved) external; + + /// @notice Queries the approval status of an operator for a given owner. + /// @param _owner The owner of the tokens + /// @param _operator Address of authorized operator + /// @return True if the operator is approved, false if not + function isApprovedForAll(address _owner, address _operator) external view returns (bool); +} diff --git a/lib/forge-std/src/interfaces/IERC165.sol b/lib/forge-std/src/interfaces/IERC165.sol new file mode 100644 index 0000000..9af4bf8 --- /dev/null +++ b/lib/forge-std/src/interfaces/IERC165.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2; + +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} diff --git a/lib/forge-std/src/interfaces/IERC20.sol b/lib/forge-std/src/interfaces/IERC20.sol new file mode 100644 index 0000000..ba40806 --- /dev/null +++ b/lib/forge-std/src/interfaces/IERC20.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2; + +/// @dev Interface of the ERC20 standard as defined in the EIP. +/// @dev This includes the optional name, symbol, and decimals metadata. +interface IERC20 { + /// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`). + event Transfer(address indexed from, address indexed to, uint256 value); + + /// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value` + /// is the new allowance. + event Approval(address indexed owner, address indexed spender, uint256 value); + + /// @notice Returns the amount of tokens in existence. + function totalSupply() external view returns (uint256); + + /// @notice Returns the amount of tokens owned by `account`. + function balanceOf(address account) external view returns (uint256); + + /// @notice Moves `amount` tokens from the caller's account to `to`. + function transfer(address to, uint256 amount) external returns (bool); + + /// @notice Returns the remaining number of tokens that `spender` is allowed + /// to spend on behalf of `owner` + function allowance(address owner, address spender) external view returns (uint256); + + /// @notice Sets `amount` as the allowance of `spender` over the caller's tokens. + /// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + function approve(address spender, uint256 amount) external returns (bool); + + /// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism. + /// `amount` is then deducted from the caller's allowance. + function transferFrom(address from, address to, uint256 amount) external returns (bool); + + /// @notice Returns the name of the token. + function name() external view returns (string memory); + + /// @notice Returns the symbol of the token. + function symbol() external view returns (string memory); + + /// @notice Returns the decimals places of the token. + function decimals() external view returns (uint8); +} diff --git a/lib/forge-std/src/interfaces/IERC4626.sol b/lib/forge-std/src/interfaces/IERC4626.sol new file mode 100644 index 0000000..bfe3a11 --- /dev/null +++ b/lib/forge-std/src/interfaces/IERC4626.sol @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2; + +import "./IERC20.sol"; + +/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in +/// https://eips.ethereum.org/EIPS/eip-4626 +interface IERC4626 is IERC20 { + event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); + + event Withdraw( + address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares + ); + + /// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + /// @dev + /// - MUST be an ERC-20 token contract. + /// - MUST NOT revert. + function asset() external view returns (address assetTokenAddress); + + /// @notice Returns the total amount of the underlying asset that is “managed” by Vault. + /// @dev + /// - SHOULD include any compounding that occurs from yield. + /// - MUST be inclusive of any fees that are charged against assets in the Vault. + /// - MUST NOT revert. + function totalAssets() external view returns (uint256 totalManagedAssets); + + /// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal + /// scenario where all the conditions are met. + /// @dev + /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + /// - MUST NOT show any variations depending on the caller. + /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + /// - MUST NOT revert. + /// + /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + /// from. + function convertToShares(uint256 assets) external view returns (uint256 shares); + + /// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal + /// scenario where all the conditions are met. + /// @dev + /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + /// - MUST NOT show any variations depending on the caller. + /// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + /// - MUST NOT revert. + /// + /// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + /// “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + /// from. + function convertToAssets(uint256 shares) external view returns (uint256 assets); + + /// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, + /// through a deposit call. + /// @dev + /// - MUST return a limited value if receiver is subject to some deposit limit. + /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. + /// - MUST NOT revert. + function maxDeposit(address receiver) external view returns (uint256 maxAssets); + + /// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given + /// current on-chain conditions. + /// @dev + /// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit + /// call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called + /// in the same transaction. + /// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the + /// deposit would be accepted, regardless if the user has enough tokens approved, etc. + /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + /// - MUST NOT revert. + /// + /// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in + /// share price or some other type of condition, meaning the depositor will lose assets by depositing. + function previewDeposit(uint256 assets) external view returns (uint256 shares); + + /// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. + /// @dev + /// - MUST emit the Deposit event. + /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + /// deposit execution, and are accounted for during deposit. + /// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not + /// approving enough underlying tokens to the Vault contract, etc). + /// + /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + function deposit(uint256 assets, address receiver) external returns (uint256 shares); + + /// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. + /// @dev + /// - MUST return a limited value if receiver is subject to some mint limit. + /// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. + /// - MUST NOT revert. + function maxMint(address receiver) external view returns (uint256 maxShares); + + /// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given + /// current on-chain conditions. + /// @dev + /// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call + /// in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the + /// same transaction. + /// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint + /// would be accepted, regardless if the user has enough tokens approved, etc. + /// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + /// - MUST NOT revert. + /// + /// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in + /// share price or some other type of condition, meaning the depositor will lose assets by minting. + function previewMint(uint256 shares) external view returns (uint256 assets); + + /// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + /// @dev + /// - MUST emit the Deposit event. + /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint + /// execution, and are accounted for during mint. + /// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not + /// approving enough underlying tokens to the Vault contract, etc). + /// + /// NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + function mint(uint256 shares, address receiver) external returns (uint256 assets); + + /// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the + /// Vault, through a withdraw call. + /// @dev + /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + /// - MUST NOT revert. + function maxWithdraw(address owner) external view returns (uint256 maxAssets); + + /// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, + /// given current on-chain conditions. + /// @dev + /// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw + /// call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if + /// called + /// in the same transaction. + /// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though + /// the withdrawal would be accepted, regardless if the user has enough shares, etc. + /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + /// - MUST NOT revert. + /// + /// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in + /// share price or some other type of condition, meaning the depositor will lose assets by depositing. + function previewWithdraw(uint256 assets) external view returns (uint256 shares); + + /// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver. + /// @dev + /// - MUST emit the Withdraw event. + /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + /// withdraw execution, and are accounted for during withdraw. + /// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner + /// not having enough shares, etc). + /// + /// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + /// Those methods should be performed separately. + function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); + + /// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, + /// through a redeem call. + /// @dev + /// - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + /// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. + /// - MUST NOT revert. + function maxRedeem(address owner) external view returns (uint256 maxShares); + + /// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, + /// given current on-chain conditions. + /// @dev + /// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call + /// in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the + /// same transaction. + /// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the + /// redemption would be accepted, regardless if the user has enough shares, etc. + /// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + /// - MUST NOT revert. + /// + /// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in + /// share price or some other type of condition, meaning the depositor will lose assets by redeeming. + function previewRedeem(uint256 shares) external view returns (uint256 assets); + + /// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver. + /// @dev + /// - MUST emit the Withdraw event. + /// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + /// redeem execution, and are accounted for during redeem. + /// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner + /// not having enough shares, etc). + /// + /// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + /// Those methods should be performed separately. + function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); +} diff --git a/lib/forge-std/src/interfaces/IERC721.sol b/lib/forge-std/src/interfaces/IERC721.sol new file mode 100644 index 0000000..0a16f45 --- /dev/null +++ b/lib/forge-std/src/interfaces/IERC721.sol @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2; + +import "./IERC165.sol"; + +/// @title ERC-721 Non-Fungible Token Standard +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// Note: the ERC-165 identifier for this interface is 0x80ac58cd. +interface IERC721 is IERC165 { + /// @dev This emits when ownership of any NFT changes by any mechanism. + /// This event emits when NFTs are created (`from` == 0) and destroyed + /// (`to` == 0). Exception: during contract creation, any number of NFTs + /// may be created and assigned without emitting Transfer. At the time of + /// any transfer, the approved address for that NFT (if any) is reset to none. + event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); + + /// @dev This emits when the approved address for an NFT is changed or + /// reaffirmed. The zero address indicates there is no approved address. + /// When a Transfer event emits, this also indicates that the approved + /// address for that NFT (if any) is reset to none. + event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); + + /// @dev This emits when an operator is enabled or disabled for an owner. + /// The operator can manage all NFTs of the owner. + event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); + + /// @notice Count all NFTs assigned to an owner + /// @dev NFTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param _owner An address for whom to query the balance + /// @return The number of NFTs owned by `_owner`, possibly zero + function balanceOf(address _owner) external view returns (uint256); + + /// @notice Find the owner of an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param _tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 _tokenId) external view returns (address); + + /// @notice Transfers the ownership of an NFT from one address to another address + /// @dev Throws unless `msg.sender` is the current owner, an authorized + /// operator, or the approved address for this NFT. Throws if `_from` is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. When transfer is complete, this function + /// checks if `_to` is a smart contract (code size > 0). If so, it calls + /// `onERC721Received` on `_to` and throws if the return value is not + /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + /// @param data Additional data with no specified format, sent in call to `_to` + function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable; + + /// @notice Transfers the ownership of an NFT from one address to another address + /// @dev This works identically to the other function with an extra data parameter, + /// except this function just sets data to "". + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; + + /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE + /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE + /// THEY MAY BE PERMANENTLY LOST + /// @dev Throws unless `msg.sender` is the current owner, an authorized + /// operator, or the approved address for this NFT. Throws if `_from` is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. + /// @param _from The current owner of the NFT + /// @param _to The new owner + /// @param _tokenId The NFT to transfer + function transferFrom(address _from, address _to, uint256 _tokenId) external payable; + + /// @notice Change or reaffirm the approved address for an NFT + /// @dev The zero address indicates there is no approved address. + /// Throws unless `msg.sender` is the current NFT owner, or an authorized + /// operator of the current owner. + /// @param _approved The new approved NFT controller + /// @param _tokenId The NFT to approve + function approve(address _approved, uint256 _tokenId) external payable; + + /// @notice Enable or disable approval for a third party ("operator") to manage + /// all of `msg.sender`'s assets + /// @dev Emits the ApprovalForAll event. The contract MUST allow + /// multiple operators per owner. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if the operator is approved, false to revoke approval + function setApprovalForAll(address _operator, bool _approved) external; + + /// @notice Get the approved address for a single NFT + /// @dev Throws if `_tokenId` is not a valid NFT. + /// @param _tokenId The NFT to find the approved address for + /// @return The approved address for this NFT, or the zero address if there is none + function getApproved(uint256 _tokenId) external view returns (address); + + /// @notice Query if an address is an authorized operator for another address + /// @param _owner The address that owns the NFTs + /// @param _operator The address that acts on behalf of the owner + /// @return True if `_operator` is an approved operator for `_owner`, false otherwise + function isApprovedForAll(address _owner, address _operator) external view returns (bool); +} + +/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02. +interface IERC721TokenReceiver { + /// @notice Handle the receipt of an NFT + /// @dev The ERC721 smart contract calls this function on the recipient + /// after a `transfer`. This function MAY throw to revert and reject the + /// transfer. Return of other than the magic value MUST result in the + /// transaction being reverted. + /// Note: the contract address is always the message sender. + /// @param _operator The address which called `safeTransferFrom` function + /// @param _from The address which previously owned the token + /// @param _tokenId The NFT identifier which is being transferred + /// @param _data Additional data with no specified format + /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` + /// unless throwing + function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) + external + returns (bytes4); +} + +/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// Note: the ERC-165 identifier for this interface is 0x5b5e139f. +interface IERC721Metadata is IERC721 { + /// @notice A descriptive name for a collection of NFTs in this contract + function name() external view returns (string memory _name); + + /// @notice An abbreviated name for NFTs in this contract + function symbol() external view returns (string memory _symbol); + + /// @notice A distinct Uniform Resource Identifier (URI) for a given asset. + /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC + /// 3986. The URI may point to a JSON file that conforms to the "ERC721 + /// Metadata JSON Schema". + function tokenURI(uint256 _tokenId) external view returns (string memory); +} + +/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension +/// @dev See https://eips.ethereum.org/EIPS/eip-721 +/// Note: the ERC-165 identifier for this interface is 0x780e9d63. +interface IERC721Enumerable is IERC721 { + /// @notice Count NFTs tracked by this contract + /// @return A count of valid NFTs tracked by this contract, where each one of + /// them has an assigned and queryable owner not equal to the zero address + function totalSupply() external view returns (uint256); + + /// @notice Enumerate valid NFTs + /// @dev Throws if `_index` >= `totalSupply()`. + /// @param _index A counter less than `totalSupply()` + /// @return The token identifier for the `_index`th NFT, + /// (sort order not specified) + function tokenByIndex(uint256 _index) external view returns (uint256); + + /// @notice Enumerate NFTs assigned to an owner + /// @dev Throws if `_index` >= `balanceOf(_owner)` or if + /// `_owner` is the zero address, representing invalid NFTs. + /// @param _owner An address where we are interested in NFTs owned by them + /// @param _index A counter less than `balanceOf(_owner)` + /// @return The token identifier for the `_index`th NFT assigned to `_owner`, + /// (sort order not specified) + function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256); +} diff --git a/lib/forge-std/test/StdAssertions.t.sol b/lib/forge-std/test/StdAssertions.t.sol new file mode 100644 index 0000000..4d5827d --- /dev/null +++ b/lib/forge-std/test/StdAssertions.t.sol @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../src/Test.sol"; + +contract StdAssertionsTest is Test { + string constant CUSTOM_ERROR = "guh!"; + + bool constant EXPECT_PASS = false; + bool constant EXPECT_FAIL = true; + + TestTest t = new TestTest(); + + /*////////////////////////////////////////////////////////////////////////// + FAIL(STRING) + //////////////////////////////////////////////////////////////////////////*/ + + function testShouldFail() external { + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._fail(CUSTOM_ERROR); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_FALSE + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertFalse_Pass() external { + t._assertFalse(false, EXPECT_PASS); + } + + function testAssertFalse_Fail() external { + vm.expectEmit(false, false, false, true); + emit log("Error: Assertion Failed"); + t._assertFalse(true, EXPECT_FAIL); + } + + function testAssertFalse_Err_Pass() external { + t._assertFalse(false, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertFalse_Err_Fail() external { + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertFalse(true, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(BOOL) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_Bool_Pass(bool a) external { + t._assertEq(a, a, EXPECT_PASS); + } + + function testAssertEq_Bool_Fail(bool a, bool b) external { + vm.assume(a != b); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [bool]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_BoolErr_Pass(bool a) external { + t._assertEq(a, a, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertEq_BoolErr_Fail(bool a, bool b) external { + vm.assume(a != b); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(BYTES) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_Bytes_Pass(bytes calldata a) external { + t._assertEq(a, a, EXPECT_PASS); + } + + function testAssertEq_Bytes_Fail(bytes calldata a, bytes calldata b) external { + vm.assume(keccak256(a) != keccak256(b)); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [bytes]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_BytesErr_Pass(bytes calldata a) external { + t._assertEq(a, a, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertEq_BytesErr_Fail(bytes calldata a, bytes calldata b) external { + vm.assume(keccak256(a) != keccak256(b)); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(ARRAY) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_UintArr_Pass(uint256 e0, uint256 e1, uint256 e2) public { + uint256[] memory a = new uint256[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + uint256[] memory b = new uint256[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_IntArr_Pass(int256 e0, int256 e1, int256 e2) public { + int256[] memory a = new int256[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + int256[] memory b = new int256[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_AddressArr_Pass(address e0, address e1, address e2) public { + address[] memory a = new address[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + address[] memory b = new address[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_UintArr_FailEl(uint256 e1) public { + vm.assume(e1 != 0); + uint256[] memory a = new uint256[](3); + uint256[] memory b = new uint256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_IntArr_FailEl(int256 e1) public { + vm.assume(e1 != 0); + int256[] memory a = new int256[](3); + int256[] memory b = new int256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_AddressArr_FailEl(address e1) public { + vm.assume(e1 != address(0)); + address[] memory a = new address[](3); + address[] memory b = new address[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_UintArrErr_FailEl(uint256 e1) public { + vm.assume(e1 != 0); + uint256[] memory a = new uint256[](3); + uint256[] memory b = new uint256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_IntArrErr_FailEl(int256 e1) public { + vm.assume(e1 != 0); + int256[] memory a = new int256[](3); + int256[] memory b = new int256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_AddressArrErr_FailEl(address e1) public { + vm.assume(e1 != address(0)); + address[] memory a = new address[](3); + address[] memory b = new address[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_UintArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + uint256[] memory a = new uint256[](lenA); + uint256[] memory b = new uint256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_IntArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + int256[] memory a = new int256[](lenA); + int256[] memory b = new int256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_AddressArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + address[] memory a = new address[](lenA); + address[] memory b = new address[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_UintArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + uint256[] memory a = new uint256[](lenA); + uint256[] memory b = new uint256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_IntArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + int256[] memory a = new int256[](lenA); + int256[] memory b = new int256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_AddressArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + address[] memory a = new address[](lenA); + address[] memory b = new address[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEqUint() public { + assertEqUint(uint8(1), uint128(1)); + assertEqUint(uint64(2), uint64(2)); + } + + function testFailAssertEqUint() public { + assertEqUint(uint64(1), uint96(2)); + assertEqUint(uint160(3), uint160(4)); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_ABS(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqAbs_Uint_Pass(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_PASS); + } + + function testAssertApproxEqAbs_Uint_Fail(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [uint]"); + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_FAIL); + } + + function testAssertApproxEqAbs_UintErr_Pass(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqAbs_UintErr_Fail(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_ABS(INT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqAbs_Int_Pass(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_PASS); + } + + function testAssertApproxEqAbs_Int_Fail(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [int]"); + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_FAIL); + } + + function testAssertApproxEqAbs_IntErr_Pass(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqAbs_IntErr_Fail(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_REL(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqRel_Uint_Pass(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_PASS); + } + + function testAssertApproxEqRel_Uint_Fail(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [uint]"); + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_FAIL); + } + + function testAssertApproxEqRel_UintErr_Pass(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqRel_UintErr_Fail(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_REL(INT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqRel_Int_Pass(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_PASS); + } + + function testAssertApproxEqRel_Int_Fail(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [int]"); + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_FAIL); + } + + function testAssertApproxEqRel_IntErr_Pass(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqRel_IntErr_Fail(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_FAIL); + } +} + +contract TestTest is Test { + modifier expectFailure(bool expectFail) { + bool preState = vm.load(HEVM_ADDRESS, bytes32("failed")) != bytes32(0x00); + _; + bool postState = vm.load(HEVM_ADDRESS, bytes32("failed")) != bytes32(0x00); + + if (preState == true) { + return; + } + + if (expectFail) { + require(postState == true, "expected failure not triggered"); + + // unwind the expected failure + vm.store(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x00))); + } else { + require(postState == false, "unexpected failure was triggered"); + } + } + + function _fail(string memory err) external expectFailure(true) { + fail(err); + } + + function _assertFalse(bool data, bool expectFail) external expectFailure(expectFail) { + assertFalse(data); + } + + function _assertFalse(bool data, string memory err, bool expectFail) external expectFailure(expectFail) { + assertFalse(data, err); + } + + function _assertEq(bool a, bool b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(bool a, bool b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(bytes memory a, bytes memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(bytes memory a, bytes memory b, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertEq(a, b, err); + } + + function _assertEq(uint256[] memory a, uint256[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(int256[] memory a, int256[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(address[] memory a, address[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(uint256[] memory a, uint256[] memory b, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertEq(a, b, err); + } + + function _assertEq(int256[] memory a, int256[] memory b, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertEq(a, b, err); + } + + function _assertEq(address[] memory a, address[] memory b, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertEq(a, b, err); + } + + function _assertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqAbs(a, b, maxDelta); + } + + function _assertApproxEqAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqAbs(a, b, maxDelta, err); + } + + function _assertApproxEqAbs(int256 a, int256 b, uint256 maxDelta, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqAbs(a, b, maxDelta); + } + + function _assertApproxEqAbs(int256 a, int256 b, uint256 maxDelta, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqAbs(a, b, maxDelta, err); + } + + function _assertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqRel(a, b, maxPercentDelta); + } + + function _assertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqRel(a, b, maxPercentDelta, err); + } + + function _assertApproxEqRel(int256 a, int256 b, uint256 maxPercentDelta, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqRel(a, b, maxPercentDelta); + } + + function _assertApproxEqRel(int256 a, int256 b, uint256 maxPercentDelta, string memory err, bool expectFail) + external + expectFailure(expectFail) + { + assertApproxEqRel(a, b, maxPercentDelta, err); + } +} diff --git a/lib/forge-std/test/StdChains.t.sol b/lib/forge-std/test/StdChains.t.sol new file mode 100644 index 0000000..3657510 --- /dev/null +++ b/lib/forge-std/test/StdChains.t.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../src/Test.sol"; + +contract StdChainsTest is Test { + function testChainRpcInitialization() public { + // RPCs specified in `foundry.toml` should be updated. + assertEq(getChain(1).rpcUrl, "https://mainnet.infura.io/v3/7a8769b798b642f6933f2ed52042bd70"); + assertEq(getChain("optimism_goerli").rpcUrl, "https://goerli.optimism.io/"); + assertEq(getChain("arbitrum_one_goerli").rpcUrl, "https://goerli-rollup.arbitrum.io/rpc/"); + + // Other RPCs should remain unchanged. + assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545"); + assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/6770454bc6ea42c58aac12978531b93f"); + } + + function testRpc(string memory rpcAlias) internal { + string memory rpcUrl = getChain(rpcAlias).rpcUrl; + vm.createSelectFork(rpcUrl); + } + + // Ensure we can connect to the default RPC URL for each chain. + function testRpcs() public { + testRpc("mainnet"); + testRpc("goerli"); + testRpc("sepolia"); + testRpc("optimism"); + testRpc("optimism_goerli"); + testRpc("arbitrum_one"); + testRpc("arbitrum_one_goerli"); + testRpc("arbitrum_nova"); + testRpc("polygon"); + testRpc("polygon_mumbai"); + testRpc("avalanche"); + testRpc("avalanche_fuji"); + testRpc("bnb_smart_chain"); + testRpc("bnb_smart_chain_testnet"); + testRpc("gnosis_chain"); + } + + function testChainNoDefault() public { + vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found."); + getChain("does_not_exist"); + } + + function testSetChainFirstFails() public { + vm.expectRevert("StdChains setChain(string,Chain): Chain ID 31337 already used by \"anvil\"."); + setChain("anvil2", Chain("Anvil", 31337, "URL")); + } + + function testChainBubbleUp() public { + setChain("needs_undefined_env_var", Chain("", 123456789, "")); + vm.expectRevert( + "Failed to resolve env var `UNDEFINED_RPC_URL_PLACEHOLDER` in `${UNDEFINED_RPC_URL_PLACEHOLDER}`: environment variable not found" + ); + getChain("needs_undefined_env_var"); + } + + function testCannotSetChain_ChainIdExists() public { + setChain("custom_chain", Chain("Custom Chain", 123456789, "https://custom.chain/")); + + vm.expectRevert('StdChains setChain(string,Chain): Chain ID 123456789 already used by "custom_chain".'); + + setChain("another_custom_chain", Chain("", 123456789, "")); + } + + function testSetChain() public { + setChain("custom_chain", Chain("Custom Chain", 123456789, "https://custom.chain/")); + Chain memory customChain = getChain("custom_chain"); + assertEq(customChain.name, "Custom Chain"); + assertEq(customChain.chainId, 123456789); + assertEq(customChain.rpcUrl, "https://custom.chain/"); + Chain memory chainById = getChain(123456789); + assertEq(chainById.name, customChain.name); + assertEq(chainById.chainId, customChain.chainId); + assertEq(chainById.rpcUrl, customChain.rpcUrl); + } + + function testSetNoEmptyAlias() public { + vm.expectRevert("StdChains setChain(string,Chain): Chain alias cannot be the empty string."); + setChain("", Chain("", 123456789, "")); + } + + function testSetNoChainId0() public { + vm.expectRevert("StdChains setChain(string,Chain): Chain ID cannot be 0."); + setChain("alias", Chain("", 0, "")); + } + + function testGetNoChainId0() public { + vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0."); + getChain(0); + } + + function testGetNoEmptyAlias() public { + vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string."); + getChain(""); + } + + function testChainIdNotFound() public { + vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found."); + getChain("no_such_alias"); + } + + function testChainAliasNotFound() public { + vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found."); + getChain(321); + } + + function testSetChain_ExistingOne() public { + setChain("custom_chain", Chain("Custom Chain", 123456789, "https://custom.chain/")); + assertEq(getChain(123456789).chainId, 123456789); + + setChain("custom_chain", Chain("Modified Chain", 999999999, "https://modified.chain/")); + vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found."); + getChain(123456789); + + Chain memory modifiedChain = getChain(999999999); + assertEq(modifiedChain.name, "Modified Chain"); + assertEq(modifiedChain.chainId, 999999999); + assertEq(modifiedChain.rpcUrl, "https://modified.chain/"); + } +} diff --git a/lib/forge-std/test/StdCheats.t.sol b/lib/forge-std/test/StdCheats.t.sol new file mode 100644 index 0000000..2eab3c3 --- /dev/null +++ b/lib/forge-std/test/StdCheats.t.sol @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../src/StdCheats.sol"; +import "../src/Test.sol"; +import "../src/StdJson.sol"; + +contract StdCheatsTest is Test { + Bar test; + + using stdJson for string; + + function setUp() public { + test = new Bar(); + } + + function testSkip() public { + vm.warp(100); + skip(25); + assertEq(block.timestamp, 125); + } + + function testRewind() public { + vm.warp(100); + rewind(25); + assertEq(block.timestamp, 75); + } + + function testHoax() public { + hoax(address(1337)); + test.bar{value: 100}(address(1337)); + } + + function testHoaxOrigin() public { + hoax(address(1337), address(1337)); + test.origin{value: 100}(address(1337)); + } + + function testHoaxDifferentAddresses() public { + hoax(address(1337), address(7331)); + test.origin{value: 100}(address(1337), address(7331)); + } + + function testStartHoax() public { + startHoax(address(1337)); + test.bar{value: 100}(address(1337)); + test.bar{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } + + function testStartHoaxOrigin() public { + startHoax(address(1337), address(1337)); + test.origin{value: 100}(address(1337)); + test.origin{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } + + function testChangePrank() public { + vm.startPrank(address(1337)); + test.bar(address(1337)); + changePrank(address(0xdead)); + test.bar(address(0xdead)); + changePrank(address(1337)); + test.bar(address(1337)); + vm.stopPrank(); + } + + function testMakeAddrEquivalence() public { + (address addr,) = makeAddrAndKey("1337"); + assertEq(makeAddr("1337"), addr); + } + + function testMakeAddrSigning() public { + (address addr, uint256 key) = makeAddrAndKey("1337"); + bytes32 hash = keccak256("some_message"); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(key, hash); + assertEq(ecrecover(hash, v, r, s), addr); + } + + function testDeal() public { + deal(address(this), 1 ether); + assertEq(address(this).balance, 1 ether); + } + + function testDealToken() public { + Bar barToken = new Bar(); + address bar = address(barToken); + deal(bar, address(this), 10000e18); + assertEq(barToken.balanceOf(address(this)), 10000e18); + } + + function testDealTokenAdjustTS() public { + Bar barToken = new Bar(); + address bar = address(barToken); + deal(bar, address(this), 10000e18, true); + assertEq(barToken.balanceOf(address(this)), 10000e18); + assertEq(barToken.totalSupply(), 20000e18); + deal(bar, address(this), 0, true); + assertEq(barToken.balanceOf(address(this)), 0); + assertEq(barToken.totalSupply(), 10000e18); + } + + function testDeployCode() public { + address deployed = deployCode("StdCheats.t.sol:Bar", bytes("")); + assertEq(string(getCode(deployed)), string(getCode(address(test)))); + } + + function testDeployCodeNoArgs() public { + address deployed = deployCode("StdCheats.t.sol:Bar"); + assertEq(string(getCode(deployed)), string(getCode(address(test)))); + } + + function testDeployCodeVal() public { + address deployed = deployCode("StdCheats.t.sol:Bar", bytes(""), 1 ether); + assertEq(string(getCode(deployed)), string(getCode(address(test)))); + assertEq(deployed.balance, 1 ether); + } + + function testDeployCodeValNoArgs() public { + address deployed = deployCode("StdCheats.t.sol:Bar", 1 ether); + assertEq(string(getCode(deployed)), string(getCode(address(test)))); + assertEq(deployed.balance, 1 ether); + } + + // We need this so we can call "this.deployCode" rather than "deployCode" directly + function deployCodeHelper(string memory what) external { + deployCode(what); + } + + function testDeployCodeFail() public { + vm.expectRevert(bytes("StdCheats deployCode(string): Deployment failed.")); + this.deployCodeHelper("StdCheats.t.sol:RevertingContract"); + } + + function getCode(address who) internal view returns (bytes memory o_code) { + /// @solidity memory-safe-assembly + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(who) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(who, add(o_code, 0x20), 0, size) + } + } + + function testDeriveRememberKey() public { + string memory mnemonic = "test test test test test test test test test test test junk"; + + (address deployer, uint256 privateKey) = deriveRememberKey(mnemonic, 0); + assertEq(deployer, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + assertEq(privateKey, 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80); + } + + function testBytesToUint() public { + assertEq(3, bytesToUint_test(hex"03")); + assertEq(2, bytesToUint_test(hex"02")); + assertEq(255, bytesToUint_test(hex"ff")); + assertEq(29625, bytesToUint_test(hex"73b9")); + } + + function testParseJsonTxDetail() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/fixtures/broadcast.log.json"); + string memory json = vm.readFile(path); + bytes memory transactionDetails = json.parseRaw(".transactions[0].tx"); + RawTx1559Detail memory rawTxDetail = abi.decode(transactionDetails, (RawTx1559Detail)); + Tx1559Detail memory txDetail = rawToConvertedEIP1559Detail(rawTxDetail); + assertEq(txDetail.from, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + assertEq(txDetail.to, 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512); + assertEq( + txDetail.data, + hex"23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004" + ); + assertEq(txDetail.nonce, 3); + assertEq(txDetail.txType, 2); + assertEq(txDetail.gas, 29625); + assertEq(txDetail.value, 0); + } + + function testReadEIP1559Transaction() public view { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/fixtures/broadcast.log.json"); + uint256 index = 0; + Tx1559 memory transaction = readTx1559(path, index); + transaction; + } + + function testReadEIP1559Transactions() public view { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/fixtures/broadcast.log.json"); + Tx1559[] memory transactions = readTx1559s(path); + transactions; + } + + function testReadReceipt() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/fixtures/broadcast.log.json"); + uint256 index = 5; + Receipt memory receipt = readReceipt(path, index); + assertEq( + receipt.logsBloom, + hex"00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100" + ); + } + + function testReadReceipts() public view { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/test/fixtures/broadcast.log.json"); + Receipt[] memory receipts = readReceipts(path); + receipts; + } + + function testGasMeteringModifier() public { + uint256 gas_start_normal = gasleft(); + addInLoop(); + uint256 gas_used_normal = gas_start_normal - gasleft(); + + uint256 gas_start_single = gasleft(); + addInLoopNoGas(); + uint256 gas_used_single = gas_start_single - gasleft(); + + uint256 gas_start_double = gasleft(); + addInLoopNoGasNoGas(); + uint256 gas_used_double = gas_start_double - gasleft(); + + emit log_named_uint("Normal gas", gas_used_normal); + emit log_named_uint("Single modifier gas", gas_used_single); + emit log_named_uint("Double modifier gas", gas_used_double); + assertTrue(gas_used_double + gas_used_single < gas_used_normal); + } + + function addInLoop() internal pure returns (uint256) { + uint256 b; + for (uint256 i; i < 10000; i++) { + b += i; + } + return b; + } + + function addInLoopNoGas() internal noGasMetering returns (uint256) { + return addInLoop(); + } + + function addInLoopNoGasNoGas() internal noGasMetering returns (uint256) { + return addInLoopNoGas(); + } + + function bytesToUint_test(bytes memory b) private pure returns (uint256) { + uint256 number; + for (uint256 i = 0; i < b.length; i++) { + number = number + uint256(uint8(b[i])) * (2 ** (8 * (b.length - (i + 1)))); + } + return number; + } + + function testAssumeNoPrecompiles(address addr) external { + assumeNoPrecompiles(addr, getChain("optimism_goerli").chainId); + assertTrue( + addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000)) + || addr > address(0x4200000000000000000000000000000000000800) + ); + } +} + +contract Bar { + constructor() payable { + /// `DEAL` STDCHEAT + totalSupply = 10000e18; + balanceOf[address(this)] = totalSupply; + } + + /// `HOAX` STDCHEATS + function bar(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + } + + function origin(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + require(tx.origin == expectedSender, "!prank"); + } + + function origin(address expectedSender, address expectedOrigin) public payable { + require(msg.sender == expectedSender, "!prank"); + require(tx.origin == expectedOrigin, "!prank"); + } + + /// `DEAL` STDCHEAT + mapping(address => uint256) public balanceOf; + uint256 public totalSupply; +} + +contract RevertingContract { + constructor() { + revert(); + } +} diff --git a/lib/forge-std/test/StdError.t.sol b/lib/forge-std/test/StdError.t.sol new file mode 100644 index 0000000..ccd3efa --- /dev/null +++ b/lib/forge-std/test/StdError.t.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +import "../src/StdError.sol"; +import "../src/Test.sol"; + +contract StdErrorsTest is Test { + ErrorsTest test; + + function setUp() public { + test = new ErrorsTest(); + } + + function testExpectAssertion() public { + vm.expectRevert(stdError.assertionError); + test.assertionError(); + } + + function testExpectArithmetic() public { + vm.expectRevert(stdError.arithmeticError); + test.arithmeticError(10); + } + + function testExpectDiv() public { + vm.expectRevert(stdError.divisionError); + test.divError(0); + } + + function testExpectMod() public { + vm.expectRevert(stdError.divisionError); + test.modError(0); + } + + function testExpectEnum() public { + vm.expectRevert(stdError.enumConversionError); + test.enumConversion(1); + } + + function testExpectEncodeStg() public { + vm.expectRevert(stdError.encodeStorageError); + test.encodeStgError(); + } + + function testExpectPop() public { + vm.expectRevert(stdError.popError); + test.pop(); + } + + function testExpectOOB() public { + vm.expectRevert(stdError.indexOOBError); + test.indexOOBError(1); + } + + function testExpectMem() public { + vm.expectRevert(stdError.memOverflowError); + test.mem(); + } + + function testExpectIntern() public { + vm.expectRevert(stdError.zeroVarError); + test.intern(); + } +} + +contract ErrorsTest { + enum T {T1} + + uint256[] public someArr; + bytes someBytes; + + function assertionError() public pure { + assert(false); + } + + function arithmeticError(uint256 a) public pure { + a -= 100; + } + + function divError(uint256 a) public pure { + 100 / a; + } + + function modError(uint256 a) public pure { + 100 % a; + } + + function enumConversion(uint256 a) public pure { + T(a); + } + + function encodeStgError() public { + /// @solidity memory-safe-assembly + assembly { + sstore(someBytes.slot, 1) + } + keccak256(someBytes); + } + + function pop() public { + someArr.pop(); + } + + function indexOOBError(uint256 a) public pure { + uint256[] memory t = new uint256[](0); + t[a]; + } + + function mem() public pure { + uint256 l = 2 ** 256 / 32; + new uint256[](l); + } + + function intern() public returns (uint256) { + function(uint256) internal returns (uint256) x; + x(2); + return 7; + } +} diff --git a/lib/forge-std/test/StdMath.t.sol b/lib/forge-std/test/StdMath.t.sol new file mode 100644 index 0000000..95037ea --- /dev/null +++ b/lib/forge-std/test/StdMath.t.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +import "../src/StdMath.sol"; +import "../src/Test.sol"; + +contract StdMathTest is Test { + function testGetAbs() external { + assertEq(stdMath.abs(-50), 50); + assertEq(stdMath.abs(50), 50); + assertEq(stdMath.abs(-1337), 1337); + assertEq(stdMath.abs(0), 0); + + assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1); + assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1)); + } + + function testGetAbs_Fuzz(int256 a) external { + uint256 manualAbs = getAbs(a); + + uint256 abs = stdMath.abs(a); + + assertEq(abs, manualAbs); + } + + function testGetDelta_Uint() external { + assertEq(stdMath.delta(uint256(0), uint256(0)), 0); + assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337); + assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max); + assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max); + assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max); + + assertEq(stdMath.delta(0, uint256(0)), 0); + assertEq(stdMath.delta(1337, uint256(0)), 1337); + assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max); + assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max); + assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max); + + assertEq(stdMath.delta(1337, uint256(1337)), 0); + assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0); + assertEq(stdMath.delta(5000, uint256(1250)), 3750); + } + + function testGetDelta_Uint_Fuzz(uint256 a, uint256 b) external { + uint256 manualDelta; + if (a > b) { + manualDelta = a - b; + } else { + manualDelta = b - a; + } + + uint256 delta = stdMath.delta(a, b); + + assertEq(delta, manualDelta); + } + + function testGetDelta_Int() external { + assertEq(stdMath.delta(int256(0), int256(0)), 0); + assertEq(stdMath.delta(int256(0), int256(1337)), 1337); + assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1); + assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1); + assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1); + + assertEq(stdMath.delta(0, int256(0)), 0); + assertEq(stdMath.delta(1337, int256(0)), 1337); + assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1); + assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1); + assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1); + + assertEq(stdMath.delta(-0, int256(0)), 0); + assertEq(stdMath.delta(-1337, int256(0)), 1337); + assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1); + assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1); + assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1); + + assertEq(stdMath.delta(int256(0), -0), 0); + assertEq(stdMath.delta(int256(0), -1337), 1337); + assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1); + assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1); + assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1); + + assertEq(stdMath.delta(1337, int256(1337)), 0); + assertEq(stdMath.delta(type(int256).max, type(int256).max), 0); + assertEq(stdMath.delta(type(int256).min, type(int256).min), 0); + assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max); + assertEq(stdMath.delta(5000, int256(1250)), 3750); + } + + function testGetDelta_Int_Fuzz(int256 a, int256 b) external { + uint256 absA = getAbs(a); + uint256 absB = getAbs(b); + uint256 absDelta = absA > absB ? absA - absB : absB - absA; + + uint256 manualDelta; + if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { + manualDelta = absDelta; + } + // (a < 0 && b >= 0) || (a >= 0 && b < 0) + else { + manualDelta = absA + absB; + } + + uint256 delta = stdMath.delta(a, b); + + assertEq(delta, manualDelta); + } + + function testGetPercentDelta_Uint() external { + assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18); + + assertEq(stdMath.percentDelta(1337, uint256(1337)), 0); + assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0); + assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18); + assertEq(stdMath.percentDelta(2500, uint256(2500)), 0); + assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18); + assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18); + + vm.expectRevert(stdError.divisionError); + stdMath.percentDelta(uint256(1), 0); + } + + function testGetPercentDelta_Uint_Fuzz(uint192 a, uint192 b) external { + vm.assume(b != 0); + uint256 manualDelta; + if (a > b) { + manualDelta = a - b; + } else { + manualDelta = b - a; + } + + uint256 manualPercentDelta = manualDelta * 1e18 / b; + uint256 percentDelta = stdMath.percentDelta(a, b); + + assertEq(percentDelta, manualPercentDelta); + } + + function testGetPercentDelta_Int() external { + assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18); + assertEq(stdMath.percentDelta(int256(0), -1337), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18); + + assertEq(stdMath.percentDelta(1337, int256(1337)), 0); + assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0); + assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0); + + assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down + assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down + assertEq(stdMath.percentDelta(0, int256(2500)), 1e18); + assertEq(stdMath.percentDelta(2500, int256(2500)), 0); + assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18); + assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18); + + vm.expectRevert(stdError.divisionError); + stdMath.percentDelta(int256(1), 0); + } + + function testGetPercentDelta_Int_Fuzz(int192 a, int192 b) external { + vm.assume(b != 0); + uint256 absA = getAbs(a); + uint256 absB = getAbs(b); + uint256 absDelta = absA > absB ? absA - absB : absB - absA; + + uint256 manualDelta; + if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { + manualDelta = absDelta; + } + // (a < 0 && b >= 0) || (a >= 0 && b < 0) + else { + manualDelta = absA + absB; + } + + uint256 manualPercentDelta = manualDelta * 1e18 / absB; + uint256 percentDelta = stdMath.percentDelta(a, b); + + assertEq(percentDelta, manualPercentDelta); + } + + /*////////////////////////////////////////////////////////////////////////// + HELPERS + //////////////////////////////////////////////////////////////////////////*/ + + function getAbs(int256 a) private pure returns (uint256) { + if (a < 0) { + return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a); + } + + return uint256(a); + } +} diff --git a/lib/forge-std/test/StdStorage.t.sol b/lib/forge-std/test/StdStorage.t.sol new file mode 100644 index 0000000..d4c563a --- /dev/null +++ b/lib/forge-std/test/StdStorage.t.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../src/StdStorage.sol"; +import "../src/Test.sol"; + +contract StdStorageTest is Test { + using stdStorage for StdStorage; + + StorageTest internal test; + + function setUp() public { + test = new StorageTest(); + } + + function testStorageHidden() public { + assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find()); + } + + function testStorageObvious() public { + assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find()); + } + + function testStorageCheckedWriteHidden() public { + stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100); + assertEq(uint256(test.hidden()), 100); + } + + function testStorageCheckedWriteObvious() public { + stdstore.target(address(test)).sig(test.exists.selector).checked_write(100); + assertEq(test.exists(), 100); + } + + function testStorageMapStructA() public { + uint256 slot = + stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).find(); + assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot); + } + + function testStorageMapStructB() public { + uint256 slot = + stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).find(); + assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot); + } + + function testStorageDeepMap() public { + uint256 slot = stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key( + address(this) + ).find(); + assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(5)))))), slot); + } + + function testStorageCheckedWriteDeepMap() public { + stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key(address(this)) + .checked_write(100); + assertEq(100, test.deep_map(address(this), address(this))); + } + + function testStorageDeepMapStructA() public { + uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)) + .with_key(address(this)).depth(0).find(); + assertEq( + bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 0), + bytes32(slot) + ); + } + + function testStorageDeepMapStructB() public { + uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)) + .with_key(address(this)).depth(1).find(); + assertEq( + bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 1), + bytes32(slot) + ); + } + + function testStorageCheckedWriteDeepMapStructA() public { + stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key( + address(this) + ).depth(0).checked_write(100); + (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); + assertEq(100, a); + assertEq(0, b); + } + + function testStorageCheckedWriteDeepMapStructB() public { + stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this)).with_key( + address(this) + ).depth(1).checked_write(100); + (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); + assertEq(0, a); + assertEq(100, b); + } + + function testStorageCheckedWriteMapStructA() public { + stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).checked_write(100); + (uint256 a, uint256 b) = test.map_struct(address(this)); + assertEq(a, 100); + assertEq(b, 0); + } + + function testStorageCheckedWriteMapStructB() public { + stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).checked_write(100); + (uint256 a, uint256 b) = test.map_struct(address(this)); + assertEq(a, 0); + assertEq(b, 100); + } + + function testStorageStructA() public { + uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find(); + assertEq(uint256(7), slot); + } + + function testStorageStructB() public { + uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find(); + assertEq(uint256(7) + 1, slot); + } + + function testStorageCheckedWriteStructA() public { + stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100); + (uint256 a, uint256 b) = test.basic(); + assertEq(a, 100); + assertEq(b, 1337); + } + + function testStorageCheckedWriteStructB() public { + stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100); + (uint256 a, uint256 b) = test.basic(); + assertEq(a, 1337); + assertEq(b, 100); + } + + function testStorageMapAddrFound() public { + uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find(); + assertEq(uint256(keccak256(abi.encode(address(this), uint256(1)))), slot); + } + + function testStorageMapUintFound() public { + uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find(); + assertEq(uint256(keccak256(abi.encode(100, uint256(2)))), slot); + } + + function testStorageCheckedWriteMapUint() public { + stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100); + assertEq(100, test.map_uint(100)); + } + + function testStorageCheckedWriteMapAddr() public { + stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100); + assertEq(100, test.map_addr(address(this))); + } + + function testStorageCheckedWriteMapBool() public { + stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true); + assertTrue(test.map_bool(address(this))); + } + + function testFailStorageCheckedWriteMapPacked() public { + // expect PackedSlot error but not external call so cant expectRevert + stdstore.target(address(test)).sig(test.read_struct_lower.selector).with_key(address(uint160(1337))) + .checked_write(100); + } + + function testStorageCheckedWriteMapPackedSuccess() public { + uint256 full = test.map_packed(address(1337)); + // keep upper 128, set lower 128 to 1337 + full = (full & (uint256((1 << 128) - 1) << 128)) | 1337; + stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337))).checked_write( + full + ); + assertEq(1337, test.read_struct_lower(address(1337))); + } + + function testFailStorageConst() public { + // vm.expectRevert(abi.encodeWithSignature("NotStorage(bytes4)", bytes4(keccak256("const()")))); + stdstore.target(address(test)).sig("const()").find(); + } + + function testFailStorageNativePack() public { + stdstore.target(address(test)).sig(test.tA.selector).find(); + stdstore.target(address(test)).sig(test.tB.selector).find(); + + // these both would fail + stdstore.target(address(test)).sig(test.tC.selector).find(); + stdstore.target(address(test)).sig(test.tD.selector).find(); + } + + function testStorageReadBytes32() public { + bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32(); + assertEq(val, hex"1337"); + } + + function testStorageReadBool_False() public { + bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool(); + assertEq(val, false); + } + + function testStorageReadBool_True() public { + bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool(); + assertEq(val, true); + } + + function testStorageReadBool_Revert() public { + vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); + this.readNonBoolValue(); + } + + function readNonBoolValue() public { + stdstore.target(address(test)).sig(test.tE.selector).read_bool(); + } + + function testStorageReadAddress() public { + address val = stdstore.target(address(test)).sig(test.tF.selector).read_address(); + assertEq(val, address(1337)); + } + + function testStorageReadUint() public { + uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint(); + assertEq(val, 1); + } + + function testStorageReadInt() public { + int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int(); + assertEq(val, type(int256).min); + } +} + +contract StorageTest { + uint256 public exists = 1; + mapping(address => uint256) public map_addr; + mapping(uint256 => uint256) public map_uint; + mapping(address => uint256) public map_packed; + mapping(address => UnpackedStruct) public map_struct; + mapping(address => mapping(address => uint256)) public deep_map; + mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; + UnpackedStruct public basic; + + uint248 public tA; + bool public tB; + + bool public tC = false; + uint248 public tD = 1; + + struct UnpackedStruct { + uint256 a; + uint256 b; + } + + mapping(address => bool) public map_bool; + + bytes32 public tE = hex"1337"; + address public tF = address(1337); + int256 public tG = type(int256).min; + bool public tH = true; + + constructor() { + basic = UnpackedStruct({a: 1337, b: 1337}); + + uint256 two = (1 << 128) | 1; + map_packed[msg.sender] = two; + map_packed[address(uint160(1337))] = 1 << 128; + } + + function read_struct_upper(address who) public view returns (uint256) { + return map_packed[who] >> 128; + } + + function read_struct_lower(address who) public view returns (uint256) { + return map_packed[who] & ((1 << 128) - 1); + } + + function hidden() public view returns (bytes32 t) { + bytes32 slot = keccak256("my.random.var"); + /// @solidity memory-safe-assembly + assembly { + t := sload(slot) + } + } + + function const() public pure returns (bytes32 t) { + t = bytes32(hex"1337"); + } +} diff --git a/lib/forge-std/test/StdUtils.t.sol b/lib/forge-std/test/StdUtils.t.sol new file mode 100644 index 0000000..f53a71b --- /dev/null +++ b/lib/forge-std/test/StdUtils.t.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../src/Test.sol"; + +contract StdUtilsTest is Test { + function testBound() public { + assertEq(bound(uint256(5), 0, 4), 0); + assertEq(bound(uint256(0), 69, 69), 69); + assertEq(bound(uint256(0), 68, 69), 68); + assertEq(bound(uint256(10), 150, 190), 174); + assertEq(bound(uint256(300), 2800, 3200), 3107); + assertEq(bound(uint256(9999), 1337, 6666), 4669); + } + + function testBound_WithinRange() public { + assertEq(bound(uint256(51), 50, 150), 51); + assertEq(bound(uint256(51), 50, 150), bound(bound(uint256(51), 50, 150), 50, 150)); + assertEq(bound(uint256(149), 50, 150), 149); + assertEq(bound(uint256(149), 50, 150), bound(bound(uint256(149), 50, 150), 50, 150)); + } + + function testBound_EdgeCoverage() public { + assertEq(bound(uint256(0), 50, 150), 50); + assertEq(bound(uint256(1), 50, 150), 51); + assertEq(bound(uint256(2), 50, 150), 52); + assertEq(bound(uint256(3), 50, 150), 53); + assertEq(bound(type(uint256).max, 50, 150), 150); + assertEq(bound(type(uint256).max - 1, 50, 150), 149); + assertEq(bound(type(uint256).max - 2, 50, 150), 148); + assertEq(bound(type(uint256).max - 3, 50, 150), 147); + } + + function testBound_DistributionIsEven(uint256 min, uint256 size) public { + size = size % 100 + 1; + min = bound(min, UINT256_MAX / 2, UINT256_MAX / 2 + size); + uint256 max = min + size - 1; + uint256 result; + + for (uint256 i = 1; i <= size * 4; ++i) { + // x > max + result = bound(max + i, min, max); + assertEq(result, min + (i - 1) % size); + // x < min + result = bound(min - i, min, max); + assertEq(result, max - (i - 1) % size); + } + } + + function testBound(uint256 num, uint256 min, uint256 max) public { + if (min > max) (min, max) = (max, min); + + uint256 result = bound(num, min, max); + + assertGe(result, min); + assertLe(result, max); + assertEq(result, bound(result, min, max)); + if (num >= min && num <= max) assertEq(result, num); + } + + function testBoundUint256Max() public { + assertEq(bound(0, type(uint256).max - 1, type(uint256).max), type(uint256).max - 1); + assertEq(bound(1, type(uint256).max - 1, type(uint256).max), type(uint256).max); + } + + function testCannotBoundMaxLessThanMin() public { + vm.expectRevert(bytes("StdUtils bound(uint256,uint256,uint256): Max is less than min.")); + bound(uint256(5), 100, 10); + } + + function testCannotBoundMaxLessThanMin(uint256 num, uint256 min, uint256 max) public { + vm.assume(min > max); + vm.expectRevert(bytes("StdUtils bound(uint256,uint256,uint256): Max is less than min.")); + bound(num, min, max); + } + + function testBoundInt() public { + assertEq(bound(-3, 0, 4), 2); + assertEq(bound(0, -69, -69), -69); + assertEq(bound(0, -69, -68), -68); + assertEq(bound(-10, 150, 190), 154); + assertEq(bound(-300, 2800, 3200), 2908); + assertEq(bound(9999, -1337, 6666), 1995); + } + + function testBoundInt_WithinRange() public { + assertEq(bound(51, -50, 150), 51); + assertEq(bound(51, -50, 150), bound(bound(51, -50, 150), -50, 150)); + assertEq(bound(149, -50, 150), 149); + assertEq(bound(149, -50, 150), bound(bound(149, -50, 150), -50, 150)); + } + + function testBoundInt_EdgeCoverage() public { + assertEq(bound(type(int256).min, -50, 150), -50); + assertEq(bound(type(int256).min + 1, -50, 150), -49); + assertEq(bound(type(int256).min + 2, -50, 150), -48); + assertEq(bound(type(int256).min + 3, -50, 150), -47); + assertEq(bound(type(int256).min, 10, 150), 10); + assertEq(bound(type(int256).min + 1, 10, 150), 11); + assertEq(bound(type(int256).min + 2, 10, 150), 12); + assertEq(bound(type(int256).min + 3, 10, 150), 13); + + assertEq(bound(type(int256).max, -50, 150), 150); + assertEq(bound(type(int256).max - 1, -50, 150), 149); + assertEq(bound(type(int256).max - 2, -50, 150), 148); + assertEq(bound(type(int256).max - 3, -50, 150), 147); + assertEq(bound(type(int256).max, -50, -10), -10); + assertEq(bound(type(int256).max - 1, -50, -10), -11); + assertEq(bound(type(int256).max - 2, -50, -10), -12); + assertEq(bound(type(int256).max - 3, -50, -10), -13); + } + + function testBoundInt_DistributionIsEven(int256 min, uint256 size) public { + size = size % 100 + 1; + min = bound(min, -int256(size / 2), int256(size - size / 2)); + int256 max = min + int256(size) - 1; + int256 result; + + for (uint256 i = 1; i <= size * 4; ++i) { + // x > max + result = bound(max + int256(i), min, max); + assertEq(result, min + int256((i - 1) % size)); + // x < min + result = bound(min - int256(i), min, max); + assertEq(result, max - int256((i - 1) % size)); + } + } + + function testBoundInt(int256 num, int256 min, int256 max) public { + if (min > max) (min, max) = (max, min); + + int256 result = bound(num, min, max); + + assertGe(result, min); + assertLe(result, max); + assertEq(result, bound(result, min, max)); + if (num >= min && num <= max) assertEq(result, num); + } + + function testBoundIntInt256Max() public { + assertEq(bound(0, type(int256).max - 1, type(int256).max), type(int256).max - 1); + assertEq(bound(1, type(int256).max - 1, type(int256).max), type(int256).max); + } + + function testBoundIntInt256Min() public { + assertEq(bound(0, type(int256).min, type(int256).min + 1), type(int256).min); + assertEq(bound(1, type(int256).min, type(int256).min + 1), type(int256).min + 1); + } + + function testCannotBoundIntMaxLessThanMin() public { + vm.expectRevert(bytes("StdUtils bound(int256,int256,int256): Max is less than min.")); + bound(-5, 100, 10); + } + + function testCannotBoundIntMaxLessThanMin(int256 num, int256 min, int256 max) public { + vm.assume(min > max); + vm.expectRevert(bytes("StdUtils bound(int256,int256,int256): Max is less than min.")); + bound(num, min, max); + } + + function testGenerateCreateAddress() external { + address deployer = 0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9; + uint256 nonce = 14; + address createAddress = computeCreateAddress(deployer, nonce); + assertEq(createAddress, 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45); + } + + function testGenerateCreate2Address() external { + bytes32 salt = bytes32(uint256(31415)); + bytes32 initcodeHash = keccak256(abi.encode(0x6080)); + address deployer = 0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9; + address create2Address = computeCreate2Address(salt, initcodeHash, deployer); + assertEq(create2Address, 0xB147a5d25748fda14b463EB04B111027C290f4d3); + } + + function testBytesToUint() external { + bytes memory maxUint = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + bytes memory two = hex"02"; + bytes memory millionEther = hex"d3c21bcecceda1000000"; + + assertEq(bytesToUint(maxUint), type(uint256).max); + assertEq(bytesToUint(two), 2); + assertEq(bytesToUint(millionEther), 1_000_000 ether); + } + + function testCannotConvertGT32Bytes() external { + bytes memory thirty3Bytes = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + vm.expectRevert("StdUtils bytesToUint(bytes): Bytes length exceeds 32."); + bytesToUint(thirty3Bytes); + } +} diff --git a/lib/forge-std/test/compilation/CompilationScript.sol b/lib/forge-std/test/compilation/CompilationScript.sol new file mode 100644 index 0000000..e205cff --- /dev/null +++ b/lib/forge-std/test/compilation/CompilationScript.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +import "../../src/Script.sol"; + +// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing +// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 +contract CompilationScript is Script {} diff --git a/lib/forge-std/test/compilation/CompilationScriptBase.sol b/lib/forge-std/test/compilation/CompilationScriptBase.sol new file mode 100644 index 0000000..ce8e0e9 --- /dev/null +++ b/lib/forge-std/test/compilation/CompilationScriptBase.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +import "../../src/Script.sol"; + +// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing +// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 +contract CompilationScriptBase is ScriptBase {} diff --git a/lib/forge-std/test/compilation/CompilationTest.sol b/lib/forge-std/test/compilation/CompilationTest.sol new file mode 100644 index 0000000..9beeafe --- /dev/null +++ b/lib/forge-std/test/compilation/CompilationTest.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +import "../../src/Test.sol"; + +// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing +// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 +contract CompilationTest is Test {} diff --git a/lib/forge-std/test/compilation/CompilationTestBase.sol b/lib/forge-std/test/compilation/CompilationTestBase.sol new file mode 100644 index 0000000..e993535 --- /dev/null +++ b/lib/forge-std/test/compilation/CompilationTestBase.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +import "../../src/Test.sol"; + +// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing +// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207 +contract CompilationTestBase is TestBase {} diff --git a/lib/forge-std/test/fixtures/broadcast.log.json b/lib/forge-std/test/fixtures/broadcast.log.json new file mode 100644 index 0000000..0a0200b --- /dev/null +++ b/lib/forge-std/test/fixtures/broadcast.log.json @@ -0,0 +1,187 @@ +{ + "transactions": [ + { + "hash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "function": "multiple_arguments(uint256,address,uint256[]):(uint256)", + "arguments": ["1", "0000000000000000000000000000000000001337", "[3,4]"], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "gas": "0x73b9", + "value": "0x0", + "data": "0x23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004", + "nonce": "0x3", + "accessList": [] + } + }, + { + "hash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "function": "inc():(uint256)", + "arguments": [], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "gas": "0xdcb2", + "value": "0x0", + "data": "0x371303c0", + "nonce": "0x4", + "accessList": [] + } + }, + { + "hash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "function": "t(uint256):(uint256)", + "arguments": ["1"], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "gas": "0x8599", + "value": "0x0", + "data": "0xafe29f710000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x5", + "accessList": [] + } + } + ], + "receipts": [ + { + "transactionHash": "0x481dc86e40bba90403c76f8e144aa9ff04c1da2164299d0298573835f0991181", + "transactionIndex": "0x0", + "blockHash": "0xef0730448490304e5403be0fa8f8ce64f118e9adcca60c07a2ae1ab921d748af", + "blockNumber": "0x1", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "cumulativeGasUsed": "0x13f3a", + "gasUsed": "0x13f3a", + "contractAddress": "0x5fbdb2315678afecb367f032d93f642f64180aa3", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x6a187183545b8a9e7f1790e847139379bf5622baff2cb43acf3f5c79470af782", + "transactionIndex": "0x0", + "blockHash": "0xf3acb96a90071640c2a8c067ae4e16aad87e634ea8d8bbbb5b352fba86ba0148", + "blockNumber": "0x2", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "cumulativeGasUsed": "0x45d80", + "gasUsed": "0x45d80", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x064ad173b4867bdef2fb60060bbdaf01735fbf10414541ea857772974e74ea9d", + "transactionIndex": "0x0", + "blockHash": "0x8373d02109d3ee06a0225f23da4c161c656ccc48fe0fcee931d325508ae73e58", + "blockNumber": "0x3", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "cumulativeGasUsed": "0x45feb", + "gasUsed": "0x45feb", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", + "transactionIndex": "0x0", + "blockHash": "0x16712fae5c0e18f75045f84363fb6b4d9a9fe25e660c4ce286833a533c97f629", + "blockNumber": "0x4", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "cumulativeGasUsed": "0x5905", + "gasUsed": "0x5905", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", + "transactionIndex": "0x0", + "blockHash": "0x156b88c3eb9a1244ba00a1834f3f70de735b39e3e59006dd03af4fe7d5480c11", + "blockNumber": "0x5", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "cumulativeGasUsed": "0xa9c4", + "gasUsed": "0xa9c4", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "transactionIndex": "0x0", + "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", + "blockNumber": "0x6", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "cumulativeGasUsed": "0x66c5", + "gasUsed": "0x66c5", + "contractAddress": null, + "logs": [ + { + "address": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "topics": [ + "0x0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000", + "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", + "blockNumber": "0x6", + "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "transactionIndex": "0x1", + "logIndex": "0x0", + "transactionLogIndex": "0x0", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x11fbb10230c168ca1e36a7e5c69a6dbcd04fd9e64ede39d10a83e36ee8065c16", + "transactionIndex": "0x0", + "blockHash": "0xf1e0ed2eda4e923626ec74621006ed50b3fc27580dc7b4cf68a07ca77420e29c", + "blockNumber": "0x7", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x0000000000000000000000000000000000001337", + "cumulativeGasUsed": "0x5208", + "gasUsed": "0x5208", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + } + ], + "libraries": [ + "src/Broadcast.t.sol:F:0x5fbdb2315678afecb367f032d93f642f64180aa3" + ], + "pending": [], + "path": "broadcast/Broadcast.t.sol/31337/run-latest.json", + "returns": {}, + "timestamp": 1655140035 +} diff --git a/lib/openzeppelin-contracts/.codecov.yml b/lib/openzeppelin-contracts/.codecov.yml new file mode 100644 index 0000000..9455306 --- /dev/null +++ b/lib/openzeppelin-contracts/.codecov.yml @@ -0,0 +1,12 @@ +comment: off +github_checks: + annotations: false +coverage: + status: + patch: + default: + target: 95% + only_pulls: true + project: + default: + threshold: 1% diff --git a/lib/openzeppelin-contracts/.editorconfig b/lib/openzeppelin-contracts/.editorconfig new file mode 100644 index 0000000..f162e8d --- /dev/null +++ b/lib/openzeppelin-contracts/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = false +max_line_length = 120 + +[*.sol] +indent_size = 4 + +[*.js] +indent_size = 2 + +[*.{adoc,md}] +max_line_length = 0 diff --git a/lib/openzeppelin-contracts/.eslintrc b/lib/openzeppelin-contracts/.eslintrc new file mode 100644 index 0000000..095d275 --- /dev/null +++ b/lib/openzeppelin-contracts/.eslintrc @@ -0,0 +1,62 @@ +{ + "extends" : [ + "standard" + ], + "plugins": [ + "mocha" + ], + "env": { + "browser" : true, + "node" : true, + "mocha" : true, + "jest" : true, + }, + "globals" : { + "artifacts": false, + "contract": false, + "assert": false, + "web3": false, + "usePlugin": false, + "extendEnvironment": false, + }, + "rules": { + + // Strict mode + "strict": ["error", "global"], + + // Code style + "array-bracket-spacing": ["off"], + "camelcase": ["error", {"properties": "always"}], + "comma-dangle": ["error", "always-multiline"], + "comma-spacing": ["error", {"before": false, "after": true}], + "dot-notation": ["error", {"allowKeywords": true, "allowPattern": ""}], + "eol-last": ["error", "always"], + "eqeqeq": ["error", "smart"], + "generator-star-spacing": ["error", "before"], + "indent": ["error", 2], + "linebreak-style": ["error", "unix"], + "max-len": ["error", 120, 2], + "no-debugger": "off", + "no-dupe-args": "error", + "no-dupe-keys": "error", + "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], + "no-redeclare": ["error", {"builtinGlobals": true}], + "no-trailing-spaces": ["error", { "skipBlankLines": false }], + "no-undef": "error", + "no-use-before-define": "off", + "no-var": "error", + "object-curly-spacing": ["error", "always"], + "prefer-const": "error", + "quotes": ["error", "single"], + "semi": ["error", "always"], + "space-before-function-paren": ["error", "always"], + + "mocha/no-exclusive-tests": ["error"], + + "promise/always-return": "off", + "promise/avoid-new": "off", + }, + "parserOptions": { + "ecmaVersion": 2020 + } +} diff --git a/lib/openzeppelin-contracts/.gitattributes b/lib/openzeppelin-contracts/.gitattributes new file mode 100644 index 0000000..52031de --- /dev/null +++ b/lib/openzeppelin-contracts/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity diff --git a/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/bug_report.md b/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..2797a08 --- /dev/null +++ b/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,21 @@ +--- +name: Bug report +about: Report a bug in OpenZeppelin Contracts + +--- + + + + + +**💻 Environment** + + + +**📝 Details** + + + +**🔢 Code to reproduce bug** + + diff --git a/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/config.yml b/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..4018cef --- /dev/null +++ b/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: Questions & Support Requests + url: https://forum.openzeppelin.com/c/support/contracts/18 + about: Ask in the OpenZeppelin Forum diff --git a/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/feature_request.md b/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..ff596b0 --- /dev/null +++ b/lib/openzeppelin-contracts/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an idea for OpenZeppelin Contracts + +--- + +**🧐 Motivation** + + +**📝 Details** + + + + diff --git a/lib/openzeppelin-contracts/.github/PULL_REQUEST_TEMPLATE.md b/lib/openzeppelin-contracts/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..469c645 --- /dev/null +++ b/lib/openzeppelin-contracts/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ + + + + + +Fixes #???? + + + + + +#### PR Checklist + + + + + +- [ ] Tests +- [ ] Documentation +- [ ] Changelog entry diff --git a/lib/openzeppelin-contracts/.github/actions/gas-compare/action.yml b/lib/openzeppelin-contracts/.github/actions/gas-compare/action.yml new file mode 100644 index 0000000..e38c48e --- /dev/null +++ b/lib/openzeppelin-contracts/.github/actions/gas-compare/action.yml @@ -0,0 +1,49 @@ +name: Compare gas costs +inputs: + token: + description: github token + required: true + report: + description: report to read from + required: false + default: gasReporterOutput.json + out_report: + description: report to read + required: false + default: ${{ github.ref_name }}.gasreport.json + ref_report: + description: report to read from + required: false + default: ${{ github.base_ref }}.gasreport.json + +runs: + using: composite + steps: + - name: Download reference report + if: github.event_name == 'pull_request' + run: | + RUN_ID=`gh run list --repo ${{ github.repository }} --branch ${{ github.base_ref }} --workflow ${{ github.workflow }} --limit 100 --json 'conclusion,databaseId,event' --jq 'map(select(.conclusion=="success" and .event!="pull_request"))[0].databaseId'` + gh run download ${RUN_ID} --repo ${{ github.repository }} -n gasreport + env: + GITHUB_TOKEN: ${{ inputs.token }} + shell: bash + continue-on-error: true + id: reference + - name: Compare reports + if: steps.reference.outcome == 'success' && github.event_name == 'pull_request' + run: | + node scripts/checks/compareGasReports.js ${{ inputs.report }} ${{ inputs.ref_report }} >> $GITHUB_STEP_SUMMARY + env: + STYLE: markdown + shell: bash + - name: Rename report for upload + if: github.event_name != 'pull_request' + run: | + mv ${{ inputs.report }} ${{ inputs.out_report }} + shell: bash + - name: Save report + if: github.event_name != 'pull_request' + uses: actions/upload-artifact@v3 + with: + name: gasreport + path: ${{ inputs.out_report }} diff --git a/lib/openzeppelin-contracts/.github/actions/setup/action.yml b/lib/openzeppelin-contracts/.github/actions/setup/action.yml new file mode 100644 index 0000000..9e562eb --- /dev/null +++ b/lib/openzeppelin-contracts/.github/actions/setup/action.yml @@ -0,0 +1,20 @@ +name: Setup + +runs: + using: composite + steps: + - uses: actions/setup-node@v3 + with: + node-version: 14.x + cache: npm + - uses: actions/cache@v3 + id: cache + with: + path: '**/node_modules' + key: npm-v3-${{ hashFiles('**/package-lock.json') }} + - name: Install dependencies + run: npm ci --prefer-offline + shell: bash + if: steps.cache.outputs.cache-hit != 'true' + env: + SKIP_COMPILE: true diff --git a/lib/openzeppelin-contracts/.github/workflows/changelog.yml b/lib/openzeppelin-contracts/.github/workflows/changelog.yml new file mode 100644 index 0000000..9f2503b --- /dev/null +++ b/lib/openzeppelin-contracts/.github/workflows/changelog.yml @@ -0,0 +1,28 @@ +name: changelog + +on: + pull_request: + types: + - opened + - synchronize + - labeled + - unlabeled + +concurrency: + group: changelog-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ignore-changelog') }} + steps: + - uses: actions/checkout@v3 + - name: Check diff + run: | + git fetch origin ${{ github.base_ref }} --depth=1 + if git diff --exit-code origin/${{ github.base_ref }} -- CHANGELOG.md ; then + echo 'Missing changelog entry' + exit 1 + fi + diff --git a/lib/openzeppelin-contracts/.github/workflows/checks.yml b/lib/openzeppelin-contracts/.github/workflows/checks.yml new file mode 100644 index 0000000..81b0b8f --- /dev/null +++ b/lib/openzeppelin-contracts/.github/workflows/checks.yml @@ -0,0 +1,91 @@ +name: checks + +on: + push: + branches: + - master + - release-v* + pull_request: {} + workflow_dispatch: {} + +concurrency: + group: checks-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - run: npm run lint + + tests: + runs-on: ubuntu-latest + env: + FORCE_COLOR: 1 + GAS: true + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - name: Run tests and generate gas report + run: npm run test + - name: Check linearisation of the inheritance graph + run: npm run test:inheritance + - name: Check proceduraly generated contracts are up-to-date + if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable' + run: npm run test:generation + - name: Compare gas costs + uses: ./.github/actions/gas-compare + with: + token: ${{ github.token }} + + foundry-tests: + if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Run tests + run: forge test -vv + + coverage: + if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - run: npm run coverage + env: + NODE_OPTIONS: --max_old_space_size=4096 + - uses: codecov/codecov-action@v3 + + slither: + if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - run: rm foundry.toml + - uses: crytic/slither-action@v0.2.0 + + codespell: + if: github.repository != 'OpenZeppelin/openzeppelin-contracts-upgradeable' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run CodeSpell + uses: codespell-project/actions-codespell@v1.0 + with: + check_filenames: true + skip: package-lock.json diff --git a/lib/openzeppelin-contracts/.github/workflows/docs.yml b/lib/openzeppelin-contracts/.github/workflows/docs.yml new file mode 100644 index 0000000..526fe60 --- /dev/null +++ b/lib/openzeppelin-contracts/.github/workflows/docs.yml @@ -0,0 +1,16 @@ +name: Build Docs + +on: + push: + branches: [release-v*] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - run: bash scripts/git-user-config.sh + - run: node scripts/update-docs-branch.js + - run: git push --all origin diff --git a/lib/openzeppelin-contracts/.github/workflows/upgradeable.yml b/lib/openzeppelin-contracts/.github/workflows/upgradeable.yml new file mode 100644 index 0000000..a94f78c --- /dev/null +++ b/lib/openzeppelin-contracts/.github/workflows/upgradeable.yml @@ -0,0 +1,23 @@ +name: Upgradeable Trigger + +on: + push: + branches: + - master + - release-v* + +jobs: + trigger: + runs-on: ubuntu-latest + steps: + - id: app + uses: getsentry/action-github-app-token@v1 + with: + app_id: ${{ secrets.UPGRADEABLE_APP_ID }} + private_key: ${{ secrets.UPGRADEABLE_APP_PK }} + - run: | + curl -X POST \ + https://api.github.com/repos/OpenZeppelin/openzeppelin-contracts-upgradeable/dispatches \ + -H 'Accept: application/vnd.github.v3+json' \ + -H 'Authorization: token ${{ steps.app.outputs.token }}' \ + -d '{ "event_type": "Update", "client_payload": { "ref": "${{ github.ref }}" } }' diff --git a/lib/openzeppelin-contracts/.gitignore b/lib/openzeppelin-contracts/.gitignore new file mode 100644 index 0000000..c60c5d9 --- /dev/null +++ b/lib/openzeppelin-contracts/.gitignore @@ -0,0 +1,64 @@ +*.swp +*.swo + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +allFiredEvents +scTopics + +# Coverage directory used by tools like istanbul +coverage +coverage.json +coverageEnv + +# node-waf configuration +.lock-wscript + +# Dependency directory +node_modules + +# Debug log from npm +npm-debug.log + +# local env variables +.env + +# truffle build directory +build/ + +# macOS +.DS_Store + +# truffle +.node-xmlhttprequest-* + +# IntelliJ IDE +.idea + +# docs artifacts +docs/modules/api + +# only used to package @openzeppelin/contracts +contracts/build/ +contracts/README.md + +# temporary artifact from solidity-coverage +allFiredEvents +.coverage_artifacts +.coverage_cache +.coverage_contracts + +# hardhat +cache +artifacts + +# Certora +.certora* +.last_confs +certora_* diff --git a/lib/openzeppelin-contracts/.gitmodules b/lib/openzeppelin-contracts/.gitmodules new file mode 100644 index 0000000..aaa229f --- /dev/null +++ b/lib/openzeppelin-contracts/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "lib/erc4626-tests"] + path = lib/erc4626-tests + url = https://github.com/a16z/erc4626-tests.git diff --git a/lib/openzeppelin-contracts/.mocharc.js b/lib/openzeppelin-contracts/.mocharc.js new file mode 100644 index 0000000..920662d --- /dev/null +++ b/lib/openzeppelin-contracts/.mocharc.js @@ -0,0 +1,4 @@ +module.exports = { + require: 'hardhat/register', + timeout: 4000, +}; diff --git a/lib/openzeppelin-contracts/.prettierrc b/lib/openzeppelin-contracts/.prettierrc new file mode 100644 index 0000000..923a710 --- /dev/null +++ b/lib/openzeppelin-contracts/.prettierrc @@ -0,0 +1,13 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "overrides": [ + { + "files": "*.sol", + "options": { + "singleQuote": false, + "printWidth": 120 + } + } + ] +} diff --git a/lib/openzeppelin-contracts/.solcover.js b/lib/openzeppelin-contracts/.solcover.js new file mode 100644 index 0000000..6cf991e --- /dev/null +++ b/lib/openzeppelin-contracts/.solcover.js @@ -0,0 +1,15 @@ +module.exports = { + norpc: true, + testCommand: 'npm test', + compileCommand: 'npm run compile', + skipFiles: [ + 'mocks', + ], + providerOptions: { + default_balance_ether: '10000000000000000000000000', + }, + mocha: { + fgrep: '[skip-on-coverage]', + invert: true, + }, +} diff --git a/lib/openzeppelin-contracts/.solhint.json b/lib/openzeppelin-contracts/.solhint.json new file mode 100644 index 0000000..7729288 --- /dev/null +++ b/lib/openzeppelin-contracts/.solhint.json @@ -0,0 +1,14 @@ +{ + "rules": { + "no-unused-vars": "error", + "const-name-snakecase": "error", + "contract-name-camelcase": "error", + "event-name-camelcase": "error", + "func-name-mixedcase": "error", + "func-param-name-mixedcase": "error", + "modifier-name-mixedcase": "error", + "private-vars-leading-underscore": "error", + "var-name-mixedcase": "error", + "imports-on-top": "error" + } +} diff --git a/lib/openzeppelin-contracts/CHANGELOG.md b/lib/openzeppelin-contracts/CHANGELOG.md new file mode 100644 index 0000000..d95b799 --- /dev/null +++ b/lib/openzeppelin-contracts/CHANGELOG.md @@ -0,0 +1,627 @@ +# Changelog + +## Unreleased + + * Reformatted codebase with latest version of Prettier Solidity. ([#3898](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3898)) + * `ReentrancyGuard`: Add a `_reentrancyGuardEntered` function to expose the guard status. ([#3714](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3714)) + * `ERC20Votes`: optimize by using unchecked arithmetic. ([#3748](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3748)) + * `Initializable`: optimize `_disableInitializers` by using `!=` instead of `<`. ([#3787](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3787)) + * `Math`: optimize `log256` rounding check. ([#3745](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3745)) + +### Deprecations + + * `ERC20Permit`: Added the file `IERC20Permit.sol` and `ERC20Permit.sol` and deprecated `draft-IERC20Permit.sol` and `draft-ERC20Permit.sol` since [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) is no longer a Draft. Developers are encouraged to update their imports. ([#3793](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3793)) + +## 4.8.0 (2022-11-08) + + * `TimelockController`: Added a new `admin` constructor parameter that is assigned the admin role instead of the deployer account. ([#3722](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3722)) + * `Initializable`: add internal functions `_getInitializedVersion` and `_isInitializing` ([#3598](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3598)) + * `ERC165Checker`: add `supportsERC165InterfaceUnchecked` for consulting individual interfaces without the full ERC165 protocol. ([#3339](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3339)) + * `Address`: optimize `functionCall` by calling `functionCallWithValue` directly. ([#3468](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3468)) + * `Address`: optimize `functionCall` functions by checking contract size only if there is no returned data. ([#3469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3469)) + * `Governor`: make the `relay` function payable, and add support for EOA payments. ([#3730](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3730)) + * `GovernorCompatibilityBravo`: remove unused `using` statements. ([#3506](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3506)) + * `ERC20`: optimize `_transfer`, `_mint` and `_burn` by using `unchecked` arithmetic when possible. ([#3513](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3513)) + * `ERC20Votes`, `ERC721Votes`: optimize `getPastVotes` for looking up recent checkpoints. ([#3673](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3673)) + * `ERC20FlashMint`: add an internal `_flashFee` function for overriding. ([#3551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3551)) + * `ERC4626`: use the same `decimals()` as the underlying asset by default (if available). ([#3639](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3639)) + * `ERC4626`: add internal `_initialConvertToShares` and `_initialConvertToAssets` functions to customize empty vaults behavior. ([#3639](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3639)) + * `ERC721`: optimize transfers by making approval clearing implicit instead of emitting an event. ([#3481](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3481)) + * `ERC721`: optimize burn by making approval clearing implicit instead of emitting an event. ([#3538](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3538)) + * `ERC721`: Fix balance accounting when a custom `_beforeTokenTransfer` hook results in a transfer of the token under consideration. ([#3611](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3611)) + * `ERC721`: use unchecked arithmetic for balance updates. ([#3524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3524)) + * `ERC721Consecutive`: Implementation of EIP-2309 that allows batch minting of ERC721 tokens during construction. ([#3311](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3311)) + * `ReentrancyGuard`: Reduce code size impact of the modifier by using internal functions. ([#3515](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3515)) + * `SafeCast`: optimize downcasting of signed integers. ([#3565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3565)) + * `ECDSA`: Remove redundant check on the `v` value. ([#3591](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3591)) + * `VestingWallet`: add `releasable` getters. ([#3580](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3580)) + * `VestingWallet`: remove unused library `Math.sol`. ([#3605](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3605)) + * `VestingWallet`: make constructor payable. ([#3665](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3665)) + * `Create2`: optimize address computation by using assembly instead of `abi.encodePacked`. ([#3600](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3600)) + * `Clones`: optimized the assembly to use only the scratch space during deployments, and optimized `predictDeterministicAddress` to use fewer operations. ([#3640](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3640)) + * `Checkpoints`: Use procedural generation to support multiple key/value lengths. ([#3589](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3589)) + * `Checkpoints`: Add new lookup mechanisms. ([#3589](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3589)) + * `Arrays`: Add `unsafeAccess` functions that allow reading and writing to an element in a storage array bypassing Solidity's "out-of-bounds" check. ([#3589](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3589)) + * `Strings`: optimize `toString`. ([#3573](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3573)) + * `Ownable2Step`: extension of `Ownable` that makes the ownership transfers a two step process. ([#3620](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3620)) + * `Math` and `SignedMath`: optimize function `max` by using `>` instead of `>=`. ([#3679](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3679)) + * `Math`: Add `log2`, `log10` and `log256`. ([#3670](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3670)) + * Arbitrum: Update the vendored arbitrum contracts to match the nitro upgrade. ([#3692](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3692)) + +### Breaking changes + + * `ERC721`: In order to add support for batch minting via `ERC721Consecutive` it was necessary to make a minor breaking change in the internal interface of `ERC721`. Namely, the hooks `_beforeTokenTransfer` and `_afterTokenTransfer` have one additional argument that may need to be added to overrides: + +```diff + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId, ++ uint256 batchSize + ) internal virtual override +``` + + * `ERC4626`: Conversion from shares to assets (and vice-versa) in an empty vault used to consider the possible mismatch between the underlying asset's and the vault's decimals. This initial conversion rate is now set to 1-to-1 irrespective of decimals, which are meant for usability purposes only. The vault now uses the assets decimals by default, so off-chain the numbers should appear the same. Developers overriding the vault decimals to a value that does not match the underlying asset may want to override the `_initialConvertToShares` and `_initialConvertToAssets` to replicate the previous behavior. + + * `TimelockController`: During deployment, the TimelockController used to grant the `TIMELOCK_ADMIN_ROLE` to the deployer and to the timelock itself. The deployer was then expected to renounce this role once configuration of the timelock is over. Failing to renounce that role allows the deployer to change the timelock permissions (but not to bypass the delay for any time-locked actions). The role is no longer given to the deployer by default. A new parameter `admin` can be set to a non-zero address to grant the admin role during construction (to the deployer or any other address). Just like previously, this admin role should be renounced after configuration. If this param is given `address(0)`, the role is not allocated and doesn't need to be revoked. In any case, the timelock itself continues to have this role. + +### Deprecations + + * `EIP712`: Added the file `EIP712.sol` and deprecated `draft-EIP712.sol` since the EIP is no longer a Draft. Developers are encouraged to update their imports. ([#3621](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3621)) + +```diff +-import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; ++import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; +``` + + * `ERC721Votes`: Added the file `ERC721Votes.sol` and deprecated `draft-ERC721Votes.sol` since it no longer depends on a Draft EIP (EIP-712). Developers are encouraged to update their imports. ([#3699](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3699)) + +```diff +-import "@openzeppelin/contracts/token/ERC721/extensions/draft-ERC721Votes.sol"; ++import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Votes.sol"; +``` + +### ERC-721 Compatibility Note + +ERC-721 integrators that interpret contract state from events should make sure that they implement the clearing of approval that is implicit in every transfer according to the EIP. Previous versions of OpenZeppelin Contracts emitted an explicit `Approval` event even though it was not required by the specification, and this is no longer the case. + +With the new `ERC721Consecutive` extension, the internal workings of `ERC721` are slightly changed. Custom extensions to ERC721 should be reviewed to ensure they remain correct. The internal functions that should be considered are `_ownerOf` (new), `_beforeTokenTransfer`, and `_afterTokenTransfer`. + +### ERC-4626 Upgrade Note + +Existing `ERC4626` contracts that are upgraded to 4.8 must initialize a new variable that holds the vault token decimals. The recommended way to do this is to use a [reinitializer]: + +[reinitializer]: https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable-reinitializer-uint8- + +```solidity +function migrateToV48() public reinitializer(2) { + __ERC4626_init(IERC20Upgradeable(asset())); +} +``` + +## 4.7.3 + +### Breaking changes + + * `ECDSA`: `recover(bytes32,bytes)` and `tryRecover(bytes32,bytes)` no longer accept compact signatures to prevent malleability. Compact signature support remains available using `recover(bytes32,bytes32,bytes32)` and `tryRecover(bytes32,bytes32,bytes32)`. + +## 4.7.2 + + * `LibArbitrumL2`, `CrossChainEnabledArbitrumL2`: Fixed detection of cross-chain calls for EOAs. Previously, calls from EOAs would be classified as cross-chain calls. ([#3578](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3578)) + * `GovernorVotesQuorumFraction`: Fixed quorum updates so they do not affect past proposals that failed due to lack of quorum. ([#3561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3561)) + * `ERC165Checker`: Added protection against large returndata. ([#3587](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3587)) + +## 4.7.1 + + * `SignatureChecker`: Fix an issue that causes `isValidSignatureNow` to revert when the target contract returns ill-encoded data. ([#3552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3552)) + * `ERC165Checker`: Fix an issue that causes `supportsInterface` to revert when the target contract returns ill-encoded data. ([#3552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3552)) + +## 4.7.0 (2022-06-29) + + * `TimelockController`: Migrate `_call` to `_execute` and allow inheritance and overriding similar to `Governor`. ([#3317](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3317)) + * `CrossChainEnabledPolygonChild`: replace the `require` statement with the custom error `NotCrossChainCall`. ([#3380](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3380)) + * `ERC20FlashMint`: Add customizable flash fee receiver. ([#3327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3327)) + * `ERC4626`: add an extension of `ERC20` that implements the ERC4626 Tokenized Vault Standard. ([#3171](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171)) + * `SafeERC20`: add `safePermit` as mitigation against phantom permit functions. ([#3280](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3280)) + * `Math`: add a `mulDiv` function that can round the result either up or down. ([#3171](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171)) + * `Math`: Add a `sqrt` function to compute square roots of integers, rounding either up or down. ([#3242](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3242)) + * `Strings`: add a new overloaded function `toHexString` that converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. ([#3403](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3403)) + * `EnumerableMap`: add new `UintToUintMap` map type. ([#3338](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3338)) + * `EnumerableMap`: add new `Bytes32ToUintMap` map type. ([#3416](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3416)) + * `SafeCast`: add support for many more types, using procedural code generation. ([#3245](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3245)) + * `MerkleProof`: add `multiProofVerify` to prove multiple values are part of a Merkle tree. ([#3276](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3276)) + * `MerkleProof`: add calldata versions of the functions to avoid copying input arrays to memory and save gas. ([#3200](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3200)) + * `ERC721`, `ERC1155`: simplified revert reasons. ([#3254](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3254), ([#3438](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3438))) + * `ERC721`: removed redundant require statement. ([#3434](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3434)) + * `PaymentSplitter`: add `releasable` getters. ([#3350](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3350)) + * `Initializable`: refactored implementation of modifiers for easier understanding. ([#3450](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3450)) + * `Proxies`: remove runtime check of ERC1967 storage slots. ([#3455](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3455)) + +### Breaking changes + + * `Initializable`: functions decorated with the modifier `reinitializer(1)` may no longer invoke each other. + +## 4.6.0 (2022-04-26) + + * `crosschain`: Add a new set of contracts for cross-chain applications. `CrossChainEnabled` is a base contract with instantiations for several chains and bridges, and `AccessControlCrossChain` is an extension of access control that allows cross-chain operation. ([#3183](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3183)) + * `AccessControl`: add a virtual `_checkRole(bytes32)` function that can be overridden to alter the `onlyRole` modifier behavior. ([#3137](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3137)) + * `EnumerableMap`: add new `AddressToUintMap` map type. ([#3150](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3150)) + * `EnumerableMap`: add new `Bytes32ToBytes32Map` map type. ([#3192](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3192)) + * `ERC20FlashMint`: support infinite allowance when paying back a flash loan. ([#3226](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3226)) + * `ERC20Wrapper`: the `decimals()` function now tries to fetch the value from the underlying token instance. If that calls revert, then the default value is used. ([#3259](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3259)) + * `draft-ERC20Permit`: replace `immutable` with `constant` for `_PERMIT_TYPEHASH` since the `keccak256` of string literals is treated specially and the hash is evaluated at compile time. ([#3196](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3196)) + * `ERC1155`: Add a `_afterTokenTransfer` hook for improved extensibility. ([#3166](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3166)) + * `ERC1155URIStorage`: add a new extension that implements a `_setURI` behavior similar to ERC721's `_setTokenURI`. ([#3210](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3210)) + * `DoubleEndedQueue`: a new data structure that supports efficient push and pop to both front and back, useful for FIFO and LIFO queues. ([#3153](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3153)) + * `Governor`: improved security of `onlyGovernance` modifier when using an external executor contract (e.g. a timelock) that can operate without necessarily going through the governance protocol. ([#3147](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3147)) + * `Governor`: Add a way to parameterize votes. This can be used to implement voting systems such as fractionalized voting, ERC721 based voting, or any number of other systems. The `params` argument added to `_countVote` method, and included in the newly added `_getVotes` method, can be used by counting and voting modules respectively for such purposes. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043)) + * `Governor`: rewording of revert reason for consistency. ([#3275](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3275)) + * `Governor`: fix an inconsistency in data locations that could lead to invalid bytecode being produced. ([#3295](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3295)) + * `Governor`: Implement `IERC721Receiver` and `IERC1155Receiver` to improve token custody by governors. ([#3230](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3230)) + * `TimelockController`: Implement `IERC721Receiver` and `IERC1155Receiver` to improve token custody by timelocks. ([#3230](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3230)) + * `TimelockController`: Add a separate canceller role for the ability to cancel. ([#3165](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3165)) + * `Initializable`: add a reinitializer modifier that enables the initialization of new modules, added to already initialized contracts through upgradeability. ([#3232](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3232)) + * `Initializable`: add an Initialized event that tracks initialized version numbers. ([#3294](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3294)) + * `ERC2981`: make `royaltyInfo` public to allow super call in overrides. ([#3305](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3305)) + +### Upgradeability notice + +* `TimelockController`: **(Action needed)** The upgrade from <4.6 to >=4.6 introduces a new `CANCELLER_ROLE` that requires set up to be assignable. After the upgrade, only addresses with this role will have the ability to cancel. Proposers will no longer be able to cancel. Assigning cancellers can be done by an admin (including the timelock itself) once the role admin is set up. To do this, we recommend upgrading to the `TimelockControllerWith46MigrationUpgradeable` contract and then calling the `migrateTo46` function. + +### Breaking changes + +* `Governor`: Adds internal virtual `_getVotes` method that must be implemented; this is a breaking change for existing concrete extensions to `Governor`. To fix this on an existing voting module extension, rename `getVotes` to `_getVotes` and add a `bytes memory` argument. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043)) +* `Governor`: Adds `params` parameter to internal virtual `_countVote` method; this is a breaking change for existing concrete extensions to `Governor`. To fix this on an existing counting module extension, add a `bytes memory` argument to `_countVote`. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043)) +* `Governor`: Does not emit `VoteCast` event when params data is non-empty; instead emits `VoteCastWithParams` event. To fix this on an integration that consumes the `VoteCast` event, also fetch/monitor `VoteCastWithParams` events. ([#3043](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3043)) +* `Votes`: The internal virtual function `_getVotingUnits` was made `view` (which was accidentally missing). Any overrides should now be updated so they are `view` as well. + +## 4.5.0 (2022-02-09) + + * `ERC2981`: add implementation of the royalty standard, and the respective extensions for `ERC721` and `ERC1155`. ([#3012](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3012)) + * `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977)) + * Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986)) + * `Governor`: add a relay function to help recover assets sent to a governor that is not its own executor (e.g. when using a timelock). ([#2926](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2926)) + * `GovernorPreventLateQuorum`: add new module to ensure a minimum voting duration is available after the quorum is reached. ([#2973](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2973)) + * `ERC721`: improved revert reason when transferring from wrong owner. ([#2975](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2975)) + * `Votes`: Added a base contract for vote tracking with delegation. ([#2944](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2944)) + * `ERC721Votes`: Added an extension of ERC721 enabled with vote tracking and delegation. ([#2944](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2944)) + * `ERC2771Context`: use immutable storage to store the forwarder address, no longer an issue since Solidity >=0.8.8 allows reading immutable variables in the constructor. ([#2917](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2917)) + * `Base64`: add a library to parse bytes into base64 strings using `encode(bytes memory)` function, and provide examples to show how to use to build URL-safe `tokenURIs`. ([#2884](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2884)) + * `ERC20`: reduce allowance before triggering transfer. ([#3056](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3056)) + * `ERC20`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3085)) + * `ERC20`: add a `_spendAllowance` internal function. ([#3170](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3170)) + * `ERC20Burnable`: do not update allowance on `burnFrom` when allowance is `type(uint256).max`. ([#3170](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3170)) + * `ERC777`: do not update allowance on `transferFrom` when allowance is `type(uint256).max`. ([#3085](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3085)) + * `ERC777`: add a `_spendAllowance` internal function. ([#3170](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3170)) + * `SignedMath`: a new signed version of the Math library with `max`, `min`, and `average`. ([#2686](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2686)) + * `SignedMath`: add an `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984)) + * `ERC1967Upgrade`: Refactor the secure upgrade to use `ERC1822` instead of the previous rollback mechanism. This reduces code complexity and attack surface with similar security guarantees. ([#3021](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3021)) + * `UUPSUpgradeable`: Add `ERC1822` compliance to support the updated secure upgrade mechanism. ([#3021](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3021)) + * Some more functions have been made virtual to customize them via overrides. In many cases this will not imply that other functions in the contract will automatically adapt to the overridden definitions. People who wish to override should consult the source code to understand the impact and if they need to override any additional functions to achieve the desired behavior. + +### Breaking changes + +* `ERC1967Upgrade`: The function `_upgradeToAndCallSecure` was renamed to `_upgradeToAndCallUUPS`, along with the change in security mechanism described above. +* `Address`: The Solidity pragma is increased from `^0.8.0` to `^0.8.1`. This is required by the `account.code.length` syntax that replaces inline assembly. This may require users to bump their compiler version from `0.8.0` to `0.8.1` or later. Note that other parts of the code already include stricter requirements. + +## 4.4.2 (2022-01-11) + +### Bugfixes + * `GovernorCompatibilityBravo`: Fix error in the encoding of calldata for proposals submitted through the compatibility interface with explicit signatures. ([#3100](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3100)) + +## 4.4.1 (2021-12-14) + + * `Initializable`: change the existing `initializer` modifier and add a new `onlyInitializing` modifier to prevent reentrancy risk. ([#3006](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3006)) + +### Breaking change + +It is no longer possible to call an `initializer`-protected function from within another `initializer` function outside the context of a constructor. Projects using OpenZeppelin upgradeable proxies should continue to work as is, since in the common case the initializer is invoked in the constructor directly. If this is not the case for you, the suggested change is to use the new `onlyInitializing` modifier in the following way: + +```diff + contract A { +- function initialize() public initializer { ... } ++ function initialize() internal onlyInitializing { ... } + } + contract B is A { + function initialize() public initializer { + A.initialize(); + } + } +``` + +## 4.4.0 (2021-11-25) + + * `Ownable`: add an internal `_transferOwnership(address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568)) + * `AccessControl`: add internal `_grantRole(bytes32,address)` and `_revokeRole(bytes32,address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568)) + * `AccessControl`: mark `_setupRole(bytes32,address)` as deprecated in favor of `_grantRole(bytes32,address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568)) + * `AccessControlEnumerable`: hook into `_grantRole(bytes32,address)` and `_revokeRole(bytes32,address)`. ([#2946](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2946)) + * `EIP712`: cache `address(this)` to immutable storage to avoid potential issues if a vanilla contract is used in a delegatecall context. ([#2852](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2852)) + * Add internal `_setApprovalForAll` to `ERC721` and `ERC1155`. ([#2834](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2834)) + * `Governor`: shift vote start and end by one block to better match Compound's GovernorBravo and prevent voting at the Governor level if the voting snapshot is not ready. ([#2892](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2892)) + * `GovernorCompatibilityBravo`: consider quorum an inclusive rather than exclusive minimum to match Compound's GovernorBravo. ([#2974](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2974)) + * `GovernorSettings`: a new governor module that manages voting settings updatable through governance actions. ([#2904](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2904)) + * `PaymentSplitter`: now supports ERC20 assets in addition to Ether. ([#2858](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2858)) + * `ECDSA`: add a variant of `toEthSignedMessageHash` for arbitrary length message hashing. ([#2865](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2865)) + * `MerkleProof`: add a `processProof` function that returns the rebuilt root hash given a leaf and a proof. ([#2841](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2841)) + * `VestingWallet`: new contract that handles the vesting of Ether and ERC20 tokens following a customizable vesting schedule. ([#2748](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2748)) + * `Governor`: enable receiving Ether when a Timelock contract is not used. ([#2849](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2849)) + * `GovernorTimelockCompound`: fix ability to use Ether stored in the Timelock contract. ([#2849](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2849)) + +## 4.3.3 + + * `ERC1155Supply`: Handle `totalSupply` changes by hooking into `_beforeTokenTransfer` to ensure consistency of balances and supply during `IERC1155Receiver.onERC1155Received` calls. + +## 4.3.2 (2021-09-14) + + * `UUPSUpgradeable`: Add modifiers to prevent `upgradeTo` and `upgradeToAndCall` being executed on any contract that is not the active ERC1967 proxy. This prevents these functions being called on implementation contracts or minimal ERC1167 clones, in particular. + +## 4.3.1 (2021-08-26) + + * `TimelockController`: Add additional isOperationReady check. + +## 4.3.0 (2021-08-17) + + * `ERC2771Context`: use private variable from storage to store the forwarder address. Fixes issues where `_msgSender()` was not callable from constructors. ([#2754](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2754)) + * `EnumerableSet`: add `values()` functions that returns an array containing all values in a single call. ([#2768](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2768)) + * `Governor`: added a modular system of `Governor` contracts based on `GovernorAlpha` and `GovernorBravo`. ([#2672](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2672)) + * Add an `interfaces` folder containing solidity interfaces to final ERCs. ([#2517](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2517)) + * `ECDSA`: add `tryRecover` functions that will not throw if the signature is invalid, and will return an error flag instead. ([#2661](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2661)) + * `SignatureChecker`: Reduce gas usage of the `isValidSignatureNow` function for the "signature by EOA" case. ([#2661](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2661)) + +## 4.2.0 (2021-06-30) + + * `ERC20Votes`: add a new extension of the `ERC20` token with support for voting snapshots and delegation. ([#2632](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2632)) + * `ERC20VotesComp`: Variant of `ERC20Votes` that is compatible with Compound's `Comp` token interface but restricts supply to `uint96`. ([#2706](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2706)) + * `ERC20Wrapper`: add a new extension of the `ERC20` token which wraps an underlying token. Deposit and withdraw guarantee that the total supply is backed by a corresponding amount of underlying token. ([#2633](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2633)) + * Enumerables: Improve gas cost of removal in `EnumerableSet` and `EnumerableMap`. + * Enumerables: Improve gas cost of lookup in `EnumerableSet` and `EnumerableMap`. + * `Counter`: add a reset method. ([#2678](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2678)) + * Tokens: Wrap definitely safe subtractions in `unchecked` blocks. + * `Math`: Add a `ceilDiv` method for performing ceiling division. + * `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593)) + * `BitMaps`: add a new `BitMaps` library that provides a storage efficient datastructure for `uint256` to `bool` mapping with contiguous keys. ([#2710](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2710)) + +### Breaking Changes + + * `ERC20FlashMint` is no longer a Draft ERC. ([#2673](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2673))) + +**How to update:** Change your import paths by removing the `draft-` prefix from `@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20FlashMint.sol`. + +> See [Releases and Stability: Drafts](https://docs.openzeppelin.com/contracts/4.x/releases-stability#drafts). + +## 4.1.0 (2021-04-29) + + * `IERC20Metadata`: add a new extended interface that includes the optional `name()`, `symbol()` and `decimals()` functions. ([#2561](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2561)) + * `ERC777`: make reception acquirement optional in `_mint`. ([#2552](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2552)) + * `ERC20Permit`: add a `_useNonce` to enable further usage of ERC712 signatures. ([#2565](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2565)) + * `ERC20FlashMint`: add an implementation of the ERC3156 extension for flash-minting ERC20 tokens. ([#2543](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2543)) + * `SignatureChecker`: add a signature verification library that supports both EOA and ERC1271 compliant contracts as signers. ([#2532](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2532)) + * `Multicall`: add abstract contract with `multicall(bytes[] calldata data)` function to bundle multiple calls together ([#2608](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2608)) + * `ECDSA`: add support for ERC2098 short-signatures. ([#2582](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2582)) + * `AccessControl`: add an `onlyRole` modifier to restrict specific function to callers bearing a specific role. ([#2609](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2609)) + * `StorageSlot`: add a library for reading and writing primitive types to specific storage slots. ([#2542](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2542)) + * UUPS Proxies: add `UUPSUpgradeable` to implement the UUPS proxy pattern together with `EIP1967Proxy`. ([#2542](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2542)) + +### Breaking changes + +This release includes two small breaking changes in `TimelockController`. + +1. The `onlyRole` modifier in this contract was designed to let anyone through if the role was granted to `address(0)`, + allowing the possibility to to make a role "open", which can be used for `EXECUTOR_ROLE`. This modifier is now + replaced by `AccessControl.onlyRole`, which does not have this ability. The previous behavior was moved to the + modifier `TimelockController.onlyRoleOrOpenRole`. +2. It was possible to make `PROPOSER_ROLE` an open role (as described in the previous item) if it was granted to + `address(0)`. This would affect the `schedule`, `scheduleBatch`, and `cancel` operations in `TimelockController`. + This ability was removed as it does not make sense to open up the `PROPOSER_ROLE` in the same way that it does for + `EXECUTOR_ROLE`. + +## 4.0.0 (2021-03-23) + + * Now targeting the 0.8.x line of Solidity compilers. For 0.6.x (resp 0.7.x) support, use version 3.4.0 (resp 3.4.0-solc-0.7) of OpenZeppelin. + * `Context`: making `_msgData` return `bytes calldata` instead of `bytes memory` ([#2492](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2492)) + * `ERC20`: removed the `_setDecimals` function and the storage slot associated to decimals. ([#2502](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2502)) + * `Strings`: addition of a `toHexString` function. ([#2504](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2504)) + * `EnumerableMap`: change implementation to optimize for `key → value` lookups instead of enumeration. ([#2518](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2518)) + * `GSN`: deprecate GSNv1 support in favor of upcoming support for GSNv2. ([#2521](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2521)) + * `ERC165`: remove uses of storage in the base ERC165 implementation. ERC165 based contracts now use storage-less virtual functions. Old behavior remains available in the `ERC165Storage` extension. ([#2505](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2505)) + * `Initializable`: make initializer check stricter during construction. ([#2531](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2531)) + * `ERC721`: remove enumerability of tokens from the base implementation. This feature is now provided separately through the `ERC721Enumerable` extension. ([#2511](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2511)) + * `AccessControl`: removed enumerability by default for a more lightweight contract. It is now opt-in through `AccessControlEnumerable`. ([#2512](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2512)) + * Meta Transactions: add `ERC2771Context` and a `MinimalForwarder` for meta-transactions. ([#2508](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2508)) + * Overall reorganization of the contract folder to improve clarity and discoverability. ([#2503](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2503)) + * `ERC20Capped`: optimize gas usage by enforcing the check directly in `_mint`. ([#2524](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2524)) + * Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547)) + * `ERC777`: optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551)) + * `ERC721URIStorage`: add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555)) + * `AccessControl`: added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562)) + * `ERC1155`: make `uri` public so overloading function can call it using super. ([#2576](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2576)) + +### Bug fixes for beta releases + + * `AccessControlEnumerable`: Fixed `renounceRole` not updating enumerable set of addresses for a role. ([#2572](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2572)) + +### How to upgrade from 3.x + +Since this version has moved a few contracts to different directories, users upgrading from a previous version will need to adjust their import statements. To make this easier, the package includes a script that will migrate import statements automatically. After upgrading to the latest version of the package, run: + +``` +npx openzeppelin-contracts-migrate-imports +``` + +Make sure you're using git or another version control system to be able to recover from any potential error in our script. + +### How to upgrade from 4.0-beta.x + +Some further changes have been done between the different beta iterations. Transitions made during this period are configured in the `migrate-imports` script. Consequently, you can upgrade from any previous 4.0-beta.x version using the same script as described in the *How to upgrade from 3.x* section. + +## 3.4.2 + + * `TimelockController`: Add additional isOperationReady check. + +## 3.4.1 (2021-03-03) + + * `ERC721`: made `_approve` an internal function (was private). + +## 3.4.0 (2021-02-02) + + * `BeaconProxy`: added new kind of proxy that allows simultaneous atomic upgrades. ([#2411](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2411)) + * `EIP712`: added helpers to verify EIP712 typed data signatures on chain. ([#2418](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2418)) + * `ERC20Permit`: added an implementation of the ERC20 permit extension for gasless token approvals. ([#2237](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237)) + * Presets: added token presets with preminted fixed supply `ERC20PresetFixedSupply` and `ERC777PresetFixedSupply`. ([#2399](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2399)) + * `Address`: added `functionDelegateCall`, similar to the existing `functionCall`. ([#2333](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2333)) + * `Clones`: added a library for deploying EIP 1167 minimal proxies. ([#2449](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2449)) + * `Context`: moved from `contracts/GSN` to `contracts/utils`. ([#2453](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2453)) + * `PaymentSplitter`: replace usage of `.transfer()` with `Address.sendValue` for improved compatibility with smart wallets. ([#2455](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2455)) + * `UpgradeableProxy`: bubble revert reasons from initialization calls. ([#2454](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2454)) + * `SafeMath`: fix a memory allocation issue by adding new `SafeMath.tryOp(uint,uint)→(bool,uint)` functions. `SafeMath.op(uint,uint,string)→uint` are now deprecated. ([#2462](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2462)) + * `EnumerableMap`: fix a memory allocation issue by adding new `EnumerableMap.tryGet(uint)→(bool,address)` functions. `EnumerableMap.get(uint)→string` is now deprecated. ([#2462](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2462)) + * `ERC165Checker`: added batch `getSupportedInterfaces`. ([#2469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2469)) + * `RefundEscrow`: `beneficiaryWithdraw` will forward all available gas to the beneficiary. ([#2480](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2480)) + * Many view and pure functions have been made virtual to customize them via overrides. In many cases this will not imply that other functions in the contract will automatically adapt to the overridden definitions. People who wish to override should consult the source code to understand the impact and if they need to override any additional functions to achieve the desired behavior. + +### Security Fixes + + * `ERC777`: fix potential reentrancy issues for custom extensions to `ERC777`. ([#2483](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2483)) + +If you're using our implementation of ERC777 from version 3.3.0 or earlier, and you define a custom `_beforeTokenTransfer` function that writes to a storage variable, you may be vulnerable to a reentrancy attack. If you're affected and would like assistance please write to security@openzeppelin.com. [Read more in the pull request.](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2483) + +## 3.3.0 (2020-11-26) + + * Now supports both Solidity 0.6 and 0.7. Compiling with solc 0.7 will result in warnings. Install the `solc-0.7` tag to compile without warnings. + * `Address`: added `functionStaticCall`, similar to the existing `functionCall`. ([#2333](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2333)) + * `TimelockController`: added a contract to augment access control schemes with a delay. ([#2354](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2354)) + * `EnumerableSet`: added `Bytes32Set`, for sets of `bytes32`. ([#2395](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2395)) + +## 3.2.2-solc-0.7 (2020-10-28) + * Resolve warnings introduced by Solidity 0.7.4. ([#2396](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2396)) + +## 3.2.1-solc-0.7 (2020-09-15) + * `ERC777`: Remove a warning about function state visibility in Solidity 0.7. ([#2327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2327)) + +## 3.2.0 (2020-09-10) + +### New features + * Proxies: added the proxy contracts from OpenZeppelin SDK. ([#2335](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2335)) + +#### Proxy changes with respect to OpenZeppelin SDK + +Aside from upgrading them from Solidity 0.5 to 0.6, we've changed a few minor things from the proxy contracts as they were found in OpenZeppelin SDK. + +- `UpgradeabilityProxy` was renamed to `UpgradeableProxy`. +- `AdminUpgradeabilityProxy` was renamed to `TransparentUpgradeableProxy`. +- `Proxy._willFallback` was renamed to `Proxy._beforeFallback`. +- `UpgradeabilityProxy._setImplementation` and `AdminUpgradeabilityProxy._setAdmin` were made private. + +### Improvements + * `Address.isContract`: switched from `extcodehash` to `extcodesize` for less gas usage. ([#2311](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2311)) + +### Breaking changes + * `ERC20Snapshot`: switched to using `_beforeTokenTransfer` hook instead of overriding ERC20 operations. ([#2312](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2312)) + +This small change in the way we implemented `ERC20Snapshot` may affect users who are combining this contract with +other ERC20 flavors, since it no longer overrides `_transfer`, `_mint`, and `_burn`. This can result in having to remove Solidity `override(...)` specifiers in derived contracts for these functions, and to instead have to add it for `_beforeTokenTransfer`. See [Using Hooks](https://docs.openzeppelin.com/contracts/3.x/extending-contracts#using-hooks) in the documentation. + +## 3.1.0 (2020-06-23) + +### New features + * `SafeCast`: added functions to downcast signed integers (e.g. `toInt32`), improving usability of `SignedSafeMath`. ([#2243](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2243)) + * `functionCall`: new helpers that replicate Solidity's function call semantics, reducing the need to rely on `call`. ([#2264](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2264)) + * `ERC1155`: added support for a base implementation, non-standard extensions and a preset contract. ([#2014](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2014), [#2230](https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2230)) + +### Improvements + * `ReentrancyGuard`: reduced overhead of using the `nonReentrant` modifier. ([#2171](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2171)) + * `AccessControl`: added a `RoleAdminChanged` event to `_setAdminRole`. ([#2214](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2214)) + * Made all `public` functions in the token preset contracts `virtual`. ([#2257](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2257)) + +### Deprecations + * `SafeERC20`: deprecated `safeApprove`. ([#2268](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2268)) + +## 3.0.2 (2020-06-08) + +### Improvements + * Added SPX license identifier to all contracts. ([#2235](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2235)) + +## 3.0.1 (2020-04-27) + +### Bugfixes + * `ERC777`: fixed the `_approve` internal function not validating some of their arguments for non-zero addresses. ([#2213](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2213)) + +## 3.0.0 (2020-04-20) + +### New features + * `AccessControl`: new contract for managing permissions in a system, replacement for `Ownable` and `Roles`. ([#2112](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2112)) + * `SafeCast`: new functions to convert to and from signed and unsigned values: `toUint256` and `toInt256`. ([#2123](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2123)) + * `EnumerableMap`: a new data structure for key-value pairs (like `mapping`) that can be iterated over. ([#2160](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2160)) + +### Breaking changes + * `ERC721`: `burn(owner, tokenId)` was removed, use `burn(tokenId)` instead. ([#2125](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2125)) + * `ERC721`: `_checkOnERC721Received` was removed. ([#2125](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2125)) + * `ERC721`: `_transferFrom` and `_safeTransferFrom` were renamed to `_transfer` and `_safeTransfer`. ([#2162](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2162)) + * `Ownable`: removed `_transferOwnership`. ([#2162](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2162)) + * `PullPayment`, `Escrow`: `withdrawWithGas` was removed. The old `withdraw` function now forwards all gas. ([#2125](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2125)) + * `Roles` was removed, use `AccessControl` as a replacement. ([#2112](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2112)) + * `ECDSA`: when receiving an invalid signature, `recover` now reverts instead of returning the zero address. ([#2114](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2114)) + * `Create2`: added an `amount` argument to `deploy` for contracts with `payable` constructors. ([#2117](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2117)) + * `Pausable`: moved to the `utils` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `Strings`: moved to the `utils` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `Counters`: moved to the `utils` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `SignedSafeMath`: moved to the `math` directory. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `ERC20Snapshot`: moved to the `token/ERC20` directory. `snapshot` was changed into an `internal` function. ([#2122](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2122)) + * `Ownable`: moved to the `access` directory. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) + * `Ownable`: removed `isOwner`. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) + * `Secondary`: removed from the library, use `Ownable` instead. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) + * `Escrow`, `ConditionalEscrow`, `RefundEscrow`: these now use `Ownable` instead of `Secondary`, their external API changed accordingly. ([#2120](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2120)) + * `ERC20`: removed `_burnFrom`. ([#2119](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2119)) + * `Address`: removed `toPayable`, use `payable(address)` instead. ([#2133](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2133)) + * `ERC777`: `_send`, `_mint` and `_burn` now use the caller as the operator. ([#2134](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2134)) + * `ERC777`: removed `_callsTokensToSend` and `_callTokensReceived`. ([#2134](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2134)) + * `EnumerableSet`: renamed `get` to `at`. ([#2151](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2151)) + * `ERC165Checker`: functions no longer have a leading underscore. ([#2150](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2150)) + * `ERC721Metadata`, `ERC721Enumerable`: these contracts were removed, and their functionality merged into `ERC721`. ([#2160](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2160)) + * `ERC721`: added a constructor for `name` and `symbol`. ([#2160](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2160)) + * `ERC20Detailed`: this contract was removed and its functionality merged into `ERC20`. ([#2161](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2161)) + * `ERC20`: added a constructor for `name` and `symbol`. `decimals` now defaults to 18. ([#2161](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2161)) + * `Strings`: renamed `fromUint256` to `toString` ([#2188](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2188)) + +## 2.5.1 (2020-04-24) + +### Bugfixes + * `ERC777`: fixed the `_send` and `_approve` internal functions not validating some of their arguments for non-zero addresses. ([#2212](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2212)) + +## 2.5.0 (2020-02-04) + +### New features + * `SafeCast.toUintXX`: new library for integer downcasting, which allows for safe operation on smaller types (e.g. `uint32`) when combined with `SafeMath`. ([#1926](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1926)) + * `ERC721Metadata`: added `baseURI`, which can be used for dramatic gas savings when all token URIs share a prefix (e.g. `http://api.myapp.com/tokens/`). ([#1970](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1970)) + * `EnumerableSet`: new library for storing enumerable sets of values. Only `AddressSet` is supported in this release. ([#2061](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/2061)) + * `Create2`: simple library to make usage of the `CREATE2` opcode easier. ([#1744](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1744)) + +### Improvements + * `ERC777`: `_burn` is now internal, providing more flexibility and making it easier to create tokens that deflate. ([#1908](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1908)) + * `ReentrancyGuard`: greatly improved gas efficiency by using the net gas metering mechanism introduced in the Istanbul hardfork. ([#1992](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1992), [#1996](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1996)) + * `ERC777`: improve extensibility by making `_send` and related functions `internal`. ([#2027](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2027)) + * `ERC721`: improved revert reason when transferring tokens to a non-recipient contract. ([#2018](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2018)) + +### Breaking changes + * `ERC165Checker` now requires a minimum Solidity compiler version of 0.5.10. ([#1829](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1829)) + +## 2.4.0 (2019-10-29) + +### New features + * `Address.toPayable`: added a helper to convert between address types without having to resort to low-level casting. ([#1773](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1773)) + * Facilities to make metatransaction-enabled contracts through the Gas Station Network. ([#1844](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1844)) + * `Address.sendValue`: added a replacement to Solidity's `transfer`, removing the fixed gas stipend. ([#1962](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1962)) + * Added replacement for functions that don't forward all gas (which have been deprecated): ([#1976](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1976)) + * `PullPayment.withdrawPaymentsWithGas(address payable payee)` + * `Escrow.withdrawWithGas(address payable payee)` + * `SafeMath`: added support for custom error messages to `sub`, `div` and `mod` functions. ([#1828](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1828)) + +### Improvements + * `Address.isContract`: switched from `extcodesize` to `extcodehash` for less gas usage. ([#1802](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1802)) + * `ERC20` and `ERC777` updated to throw custom errors on subtraction overflows. ([#1828](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1828)) + +### Deprecations + * Deprecated functions that don't forward all gas: ([#1976](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1976)) + * `PullPayment.withdrawPayments(address payable payee)` + * `Escrow.withdraw(address payable payee)` + +### Breaking changes + * `Address` now requires a minimum Solidity compiler version of 0.5.5. ([#1802](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1802)) + * `SignatureBouncer` has been removed from drafts, both to avoid confusions with the GSN and `GSNRecipientSignature` (previously called `GSNBouncerSignature`) and because the API was not very clear. ([#1879](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1879)) + +### How to upgrade from 2.4.0-beta + +The final 2.4.0 release includes a refactor of the GSN contracts that will be a breaking change for 2.4.0-beta users. + + * The default empty implementations of `_preRelayedCall` and `_postRelayedCall` were removed and must now be explicitly implemented always in custom recipients. If your custom recipient didn't include an implementation, you can provide an empty one. + * `GSNRecipient`, `GSNBouncerBase`, and `GSNContext` were all merged into `GSNRecipient`. + * `GSNBouncerSignature` and `GSNBouncerERC20Fee` were renamed to `GSNRecipientSignature` and `GSNRecipientERC20Fee`. + * It is no longer necessary to inherit from `GSNRecipient` when using `GSNRecipientSignature` and `GSNRecipientERC20Fee`. + +For example, a contract using `GSNBouncerSignature` would have to be changed in the following way. + +```diff +-contract MyDapp is GSNRecipient, GSNBouncerSignature { ++contract MyDapp is GSNRecipientSignature { +``` + +Refer to the table below to adjust your inheritance list. + +| 2.4.0-beta | 2.4.0 | +| ---------------------------------- | ---------------------------- | +| `GSNRecipient, GSNBouncerSignature`| `GSNRecipientSignature` | +| `GSNRecipient, GSNBouncerERC20Fee` | `GSNRecipientERC20Fee` | +| `GSNBouncerBase` | `GSNRecipient` | + +## 2.3.0 (2019-05-27) + +### New features + * `ERC1820`: added support for interacting with the [ERC1820](https://eips.ethereum.org/EIPS/eip-1820) registry contract (`IERC1820Registry`), as well as base contracts that can be registered as implementers there. ([#1677](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1677)) + * `ERC777`: support for the [ERC777 token](https://eips.ethereum.org/EIPS/eip-777), which has multiple improvements over `ERC20` (but is backwards compatible with it) such as built-in burning, a more straightforward permission system, and optional sender and receiver hooks on transfer (mandatory for contracts!). ([#1684](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1684)) + * All contracts now have revert reason strings, which give insight into error conditions, and help debug failing transactions. ([#1704](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1704)) + +### Improvements + * Reverted the Solidity version bump done in v2.2.0, setting the minimum compiler version to v0.5.0, to prevent unexpected build breakage. Users are encouraged however to stay on top of new compiler releases, which usually include bugfixes. ([#1729](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1729)) + +### Bugfixes + * `PostDeliveryCrowdsale`: some validations where skipped when paired with other crowdsale flavors, such as `AllowanceCrowdsale`, or `MintableCrowdsale` and `ERC20Capped`, which could cause buyers to not be able to claim their purchased tokens. ([#1721](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1721)) + * `ERC20._transfer`: the `from` argument was allowed to be the zero address, so it was possible to internally trigger a transfer of 0 tokens from the zero address. This address is not a valid destinatary of transfers, nor can it give or receive allowance, so this behavior was inconsistent. It now reverts. ([#1752](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1752)) + +## 2.2.0 (2019-03-14) + +### New features + * `ERC20Snapshot`: create snapshots on demand of the token balances and total supply, to later retrieve and e.g. calculate dividends at a past time. ([#1617](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1617)) + * `SafeERC20`: `ERC20` contracts with no return value (i.e. that revert on failure) are now supported. ([#1655](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1655)) + * `ERC20`: added internal `_approve(address owner, address spender, uint256 value)`, allowing derived contracts to set the allowance of arbitrary accounts. ([#1609](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1609)) + * `ERC20Metadata`: added internal `_setTokenURI(string memory tokenURI)`. ([#1618](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1618)) + * `TimedCrowdsale`: added internal `_extendTime(uint256 newClosingTime)` as well as `TimedCrowdsaleExtended(uint256 prevClosingTime, uint256 newClosingTime)` event allowing to extend the crowdsale, as long as it hasn't already closed. + +### Improvements + * Upgraded the minimum compiler version to v0.5.2: this removes many Solidity warnings that were false positives. ([#1606](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1606)) + * `ECDSA`: `recover` no longer accepts malleable signatures (those using upper-range values for `s`, or 0/1 for `v`). ([#1622](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1622)) + * ``ERC721``'s transfers are now more gas efficient due to removal of unnecessary `SafeMath` calls. ([#1610](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1610)) + * Fixed variable shadowing issues. ([#1606](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1606)) + +### Bugfixes + * (minor) `SafeERC20`: `safeApprove` wasn't properly checking for a zero allowance when attempting to set a non-zero allowance. ([#1647](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1647)) + +### Breaking changes in drafts + * `TokenMetadata` has been renamed to `ERC20Metadata`. ([#1618](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1618)) + * The library `Counter` has been renamed to `Counters` and its API has been improved. See an example in `ERC721`, lines [17](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/3cb4a00fce1da76196ac0ac3a0ae9702b99642b5/contracts/token/ERC721/ERC721.sol#L17) and [204](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/3cb4a00fce1da76196ac0ac3a0ae9702b99642b5/contracts/token/ERC721/ERC721.sol#L204). ([#1610](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1610)) + +## 2.1.3 (2019-02-26) + * Backported `SafeERC20.safeApprove` bugfix. ([#1647](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1647)) + +## 2.1.2 (2019-01-17) + * Removed most of the test suite from the npm package, except `PublicRole.behavior.js`, which may be useful to users testing their own `Roles`. + +## 2.1.1 (2019-01-04) + * Version bump to avoid conflict in the npm registry. + +## 2.1.0 (2019-01-04) + +### New features + * Now targeting the 0.5.x line of Solidity compilers. For 0.4.24 support, use version 2.0 of OpenZeppelin. + * `WhitelistCrowdsale`: a crowdsale where only whitelisted accounts (`WhitelistedRole`) can purchase tokens. Adding or removing accounts from the whitelist is done by whitelist admins (`WhitelistAdminRole`). Similar to the pre-2.0 `WhitelistedCrowdsale`. ([#1525](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1525), [#1589](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1589)) + * `RefundablePostDeliveryCrowdsale`: replacement for `RefundableCrowdsale` (deprecated, see below) where tokens are only granted once the crowdsale ends (if it meets its goal). ([#1543](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1543)) + * `PausableCrowdsale`: allows for pausers (`PauserRole`) to pause token purchases. Other crowdsale operations (e.g. withdrawals and refunds, if applicable) are not affected. ([#832](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/832)) + * `ERC20`: `transferFrom` and `_burnFrom ` now emit `Approval` events, to represent the token's state comprehensively through events. ([#1524](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1524)) + * `ERC721`: added `_burn(uint256 tokenId)`, replacing the similar deprecated function (see below). ([#1550](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1550)) + * `ERC721`: added `_tokensOfOwner(address owner)`, allowing to internally retrieve the array of an account's owned tokens. ([#1522](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1522)) + * Crowdsales: all constructors are now `public`, meaning it is not necessary to extend these contracts in order to deploy them. The exception is `FinalizableCrowdsale`, since it is meaningless unless extended. ([#1564](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1564)) + * `SignedSafeMath`: added overflow-safe operations for signed integers (`int256`). ([#1559](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1559), [#1588](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1588)) + +### Improvements + * The compiler version required by `Array` was behind the rest of the library so it was updated to `v0.4.24`. ([#1553](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1553)) + * Now conforming to a 4-space indentation code style. ([1508](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1508)) + * `ERC20`: more gas efficient due to removed redundant `require`s. ([#1409](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1409)) + * `ERC721`: fixed a bug that prevented internal data structures from being properly cleaned, missing potential gas refunds. ([#1539](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1539) and [#1549](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1549)) + * `ERC721`: general gas savings on `transferFrom`, `_mint` and `_burn`, due to redundant `require`s and `SSTORE`s. ([#1549](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1549)) + +### Bugfixes + +### Breaking changes + +### Deprecations + * `ERC721._burn(address owner, uint256 tokenId)`: due to the `owner` parameter being unnecessary. ([#1550](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1550)) + * `RefundableCrowdsale`: due to trading abuse potential on crowdsales that miss their goal. ([#1543](https://github.com/OpenZeppelin/openzeppelin-solidity/pull/1543)) diff --git a/lib/openzeppelin-contracts/CODE_OF_CONDUCT.md b/lib/openzeppelin-contracts/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..86c0474 --- /dev/null +++ b/lib/openzeppelin-contracts/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at maintainers@openzeppelin.org. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/lib/openzeppelin-contracts/CONTRIBUTING.md b/lib/openzeppelin-contracts/CONTRIBUTING.md new file mode 100644 index 0000000..5012847 --- /dev/null +++ b/lib/openzeppelin-contracts/CONTRIBUTING.md @@ -0,0 +1,64 @@ +Contributing to OpenZeppelin Contracts +======= + +We really appreciate and value contributions to OpenZeppelin Contracts. Please take 5' to review the items listed below to make sure that your contributions are merged as soon as possible. + +## Contribution guidelines + +Smart contracts manage value and are highly vulnerable to errors and attacks. We have very strict [guidelines], please make sure to review them! + +## Creating Pull Requests (PRs) + +As a contributor, you are expected to fork this repository, work on your own fork and then submit pull requests. The pull requests will be reviewed and eventually merged into the main repo. See ["Fork-a-Repo"](https://help.github.com/articles/fork-a-repo/) for how this works. + +## A typical workflow + +1) Make sure your fork is up to date with the main repository: + +``` +cd openzeppelin-contracts +git remote add upstream https://github.com/OpenZeppelin/openzeppelin-contracts.git +git fetch upstream +git pull --rebase upstream master +``` +NOTE: The directory `openzeppelin-contracts` represents your fork's local copy. + +2) Branch out from `master` into `fix/some-bug-#123`: +(Postfixing #123 will associate your PR with the issue #123 and make everyone's life easier =D) +``` +git checkout -b fix/some-bug-#123 +``` + +3) Make your changes, add your files, commit, and push to your fork. + +``` +git add SomeFile.js +git commit "Fix some bug #123" +git push origin fix/some-bug-#123 +``` + +4) Run tests, linter, etc. This can be done by running local continuous integration and make sure it passes. + +```bash +npm test +npm run lint +``` + +5) Go to [github.com/OpenZeppelin/openzeppelin-contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) in your web browser and issue a new pull request. + +*IMPORTANT* Read the PR template very carefully and make sure to follow all the instructions. These instructions +refer to some very important conditions that your PR must meet in order to be accepted, such as making sure that all tests pass, JS linting tests pass, Solidity linting tests pass, etc. + +6) Maintainers will review your code and possibly ask for changes before your code is pulled in to the main repository. We'll check that all tests pass, review the coding style, and check for general code correctness. If everything is OK, we'll merge your pull request and your code will be part of OpenZeppelin Contracts. + +*IMPORTANT* Please pay attention to the maintainer's feedback, since it's a necessary step to keep up with the standards OpenZeppelin Contracts attains to. + +## All set! + +If you have any questions, feel free to post them to github.com/OpenZeppelin/openzeppelin-contracts/issues. + +Finally, if you're looking to collaborate and want to find easy tasks to start, look at the issues we marked as ["Good first issue"](https://github.com/OpenZeppelin/openzeppelin-contracts/labels/good%20first%20issue). + +Thanks for your time and code! + +[guidelines]: GUIDELINES.md diff --git a/lib/openzeppelin-contracts/DOCUMENTATION.md b/lib/openzeppelin-contracts/DOCUMENTATION.md new file mode 100644 index 0000000..ca39e51 --- /dev/null +++ b/lib/openzeppelin-contracts/DOCUMENTATION.md @@ -0,0 +1,16 @@ +Documentation is hosted at https://docs.openzeppelin.com/contracts. + +All of the content for the site is in this repository. The guides are in the +[docs](/docs) directory, and the API Reference is extracted from comments in +the source code. If you want to help improve the content, this is the +repository you should be contributing to. + +[`solidity-docgen`](https://github.com/OpenZeppelin/solidity-docgen) is the +program that extracts the API Reference from source code. + +The [`docs.openzeppelin.com`](https://github.com/OpenZeppelin/docs.openzeppelin.com) +repository hosts the configuration for the entire site, which includes +documentation for all of the OpenZeppelin projects. + +To run the docs locally you should run `npm run docs:watch` on this +repository. diff --git a/lib/openzeppelin-contracts/GUIDELINES.md b/lib/openzeppelin-contracts/GUIDELINES.md new file mode 100644 index 0000000..9706750 --- /dev/null +++ b/lib/openzeppelin-contracts/GUIDELINES.md @@ -0,0 +1,105 @@ +Design Guidelines +======= + +These are some global design goals in OpenZeppelin Contracts. + +#### D0 - Security in Depth +We strive to provide secure, tested, audited code. To achieve this, we need to match intention with function. Thus, documentation, code clarity, community review and security discussions are fundamental. + +#### D1 - Simple and Modular +Simpler code means easier audits, and better understanding of what each component does. We look for small files, small contracts, and small functions. If you can separate a contract into two independent functionalities you should probably do it. + +#### D2 - Naming Matters + +We take our time with picking names. Code is going to be written once, and read hundreds of times. Renaming for clarity is encouraged. + +#### D3 - Tests + +Write tests for all your code. We encourage Test Driven Development so we know when our code is right. Even though not all code in the repository is tested at the moment, we aim to test every line of code in the future. + +#### D4 - Check preconditions and post-conditions + +A very important way to prevent vulnerabilities is to catch a contract’s inconsistent state as early as possible. This is why we want functions to check pre- and post-conditions for executing its logic. When writing code, ask yourself what you are expecting to be true before and after the function runs, and express it in code. + +#### D5 - Code Consistency + +Consistency on the way classes are used is paramount to an easier understanding of the library. The codebase should be as unified as possible. Read existing code and get inspired before you write your own. Follow the style guidelines. Don’t hesitate to ask for help on how to best write a specific piece of code. + +#### D6 - Regular Audits +Following good programming practices is a way to reduce the risk of vulnerabilities, but professional code audits are still needed. We will perform regular code audits on major releases, and hire security professionals to provide independent review. + +# Style Guidelines + +The design guidelines have quite a high abstraction level. These style guidelines are more concrete and easier to apply, and also more opinionated. We value clean code and consistency, and those are prerequisites for us to include new code in the repository. Before proposing a change, please read these guidelines and take some time to familiarize yourself with the style of the existing codebase. + +## Solidity code + +In order to be consistent with all the other Solidity projects, we follow the +[official recommendations documented in the Solidity style guide](http://solidity.readthedocs.io/en/latest/style-guide.html). + +Any exception or additions specific to our project are documented below. + +* Try to avoid acronyms and abbreviations. + +* All state variables should be private. + +* Private state variables should have an underscore prefix. + + ``` + contract TestContract { + uint256 private _privateVar; + uint256 internal _internalVar; + } + ``` + +* Parameters must not be prefixed with an underscore. + + ``` + function test(uint256 testParameter1, uint256 testParameter2) { + ... + } + ``` + +* Internal and private functions should have an underscore prefix. + + ``` + function _testInternal() internal { + ... + } + ``` + + ``` + function _testPrivate() private { + ... + } + ``` + +* Events should be emitted immediately after the state change that they + represent, and consequently they should be named in past tense. + + ``` + function _burn(address who, uint256 value) internal { + super._burn(who, value); + emit TokensBurned(who, value); + } + ``` + + Some standards (e.g. ERC20) use present tense, and in those cases the + standard specification prevails. + +* Interface names should have a capital I prefix. + + ``` + interface IERC777 { + ``` + + +## Tests + +* Tests Must be Written Elegantly + + Tests are a good way to show how to use the library, and maintaining them is extremely necessary. Don't write long tests, write helper functions to make them be as short and concise as possible (they should take just a few lines each), and use good variable names. + +* Tests Must not be Random + + Inputs for tests should not be generated randomly. Accounts used to create test contracts are an exception, those can be random. Also, the type and structure of outputs should be checked. diff --git a/lib/openzeppelin-contracts/LICENSE b/lib/openzeppelin-contracts/LICENSE new file mode 100644 index 0000000..4f51be0 --- /dev/null +++ b/lib/openzeppelin-contracts/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2022 zOS Global Limited and contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/openzeppelin-contracts/README.md b/lib/openzeppelin-contracts/README.md new file mode 100644 index 0000000..a30fd6c --- /dev/null +++ b/lib/openzeppelin-contracts/README.md @@ -0,0 +1,84 @@ +# OpenZeppelin + +[![Docs](https://img.shields.io/badge/docs-%F0%9F%93%84-blue)](https://docs.openzeppelin.com/contracts) +[![NPM Package](https://img.shields.io/npm/v/@openzeppelin/contracts.svg)](https://www.npmjs.org/package/@openzeppelin/contracts) +[![Coverage Status](https://codecov.io/gh/OpenZeppelin/openzeppelin-contracts/graph/badge.svg)](https://codecov.io/gh/OpenZeppelin/openzeppelin-contracts) +[![gitpoap badge](https://public-api.gitpoap.io/v1/repo/OpenZeppelin/openzeppelin-contracts/badge)](https://www.gitpoap.io/gh/OpenZeppelin/openzeppelin-contracts) + +**A library for secure smart contract development.** Build on a solid foundation of community-vetted code. + + * Implementations of standards like [ERC20](https://docs.openzeppelin.com/contracts/erc20) and [ERC721](https://docs.openzeppelin.com/contracts/erc721). + * Flexible [role-based permissioning](https://docs.openzeppelin.com/contracts/access-control) scheme. + * Reusable [Solidity components](https://docs.openzeppelin.com/contracts/utilities) to build custom contracts and complex decentralized systems. + +:mage: **Not sure how to get started?** Check out [Contracts Wizard](https://wizard.openzeppelin.com/) — an interactive smart contract generator. + +:building_construction: **Want to scale your decentralized application?** Check out [OpenZeppelin Defender](https://openzeppelin.com/defender) — a secure platform for automating and monitoring your operations. + +## Overview + +### Installation + +```console +$ npm install @openzeppelin/contracts +``` + +OpenZeppelin Contracts features a [stable API](https://docs.openzeppelin.com/contracts/releases-stability#api-stability), which means that your contracts won't break unexpectedly when upgrading to a newer minor version. + +An alternative to npm is to use the GitHub repository (`openzeppelin/openzeppelin-contracts`) to retrieve the contracts. When doing this, make sure to specify the tag for a release such as `v4.5.0`, instead of using the `master` branch. + +### Usage + +Once installed, you can use the contracts in the library by importing them: + +```solidity +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract MyCollectible is ERC721 { + constructor() ERC721("MyCollectible", "MCO") { + } +} +``` + +_If you're new to smart contract development, head to [Developing Smart Contracts](https://docs.openzeppelin.com/learn/developing-smart-contracts) to learn about creating a new project and compiling your contracts._ + +To keep your system secure, you should **always** use the installed code as-is, and neither copy-paste it from online sources nor modify it yourself. The library is designed so that only the contracts and functions you use are deployed, so you don't need to worry about it needlessly increasing gas costs. + +## Learn More + +The guides in the [documentation site](https://docs.openzeppelin.com/contracts) will teach about different concepts, and how to use the related contracts that OpenZeppelin Contracts provides: + +* [Access Control](https://docs.openzeppelin.com/contracts/access-control): decide who can perform each of the actions on your system. +* [Tokens](https://docs.openzeppelin.com/contracts/tokens): create tradeable assets or collectives, and distribute them via [Crowdsales](https://docs.openzeppelin.com/contracts/crowdsales). +* [Gas Station Network](https://docs.openzeppelin.com/contracts/gsn): let your users interact with your contracts without having to pay for gas themselves. +* [Utilities](https://docs.openzeppelin.com/contracts/utilities): generic useful tools including non-overflowing math, signature verification, and trustless paying systems. + +The [full API](https://docs.openzeppelin.com/contracts/api/token/ERC20) is also thoroughly documented, and serves as a great reference when developing your smart contract application. You can also ask for help or follow Contracts's development in the [community forum](https://forum.openzeppelin.com). + +Finally, you may want to take a look at the [guides on our blog](https://blog.openzeppelin.com/guides), which cover several common use cases and good practices. The following articles provide great background reading, though please note that some of the referenced tools have changed, as the tooling in the ecosystem continues to rapidly evolve. + +* [The Hitchhiker’s Guide to Smart Contracts in Ethereum](https://blog.openzeppelin.com/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05) will help you get an overview of the various tools available for smart contract development, and help you set up your environment. +* [A Gentle Introduction to Ethereum Programming, Part 1](https://blog.openzeppelin.com/a-gentle-introduction-to-ethereum-programming-part-1-783cc7796094) provides very useful information on an introductory level, including many basic concepts from the Ethereum platform. +* For a more in-depth dive, you may read the guide [Designing the Architecture for Your Ethereum Application](https://blog.openzeppelin.com/designing-the-architecture-for-your-ethereum-application-9cec086f8317), which discusses how to better structure your application and its relationship to the real world. + +## Security + +This project is maintained by [OpenZeppelin](https://openzeppelin.com), and developed following our high standards for code quality and security. OpenZeppelin Contracts is meant to provide tested and community-audited code, but please use common sense when doing anything that deals with real money! We take no responsibility for your implementation decisions and any security problems you might experience. + +The core development principles and strategies that OpenZeppelin Contracts is based on include: security in depth, simple and modular code, clarity-driven naming conventions, comprehensive unit testing, pre-and-post-condition sanity checks, code consistency, and regular audits. + +The latest audit was done on October 2018 on version 2.0.0. + +We have a [**bug bounty program** on Immunefi](https://www.immunefi.com/bounty/openzeppelin). Please report any security issues you find through the Immunefi dashboard, or reach out to security@openzeppelin.com. + +Critical bug fixes will be backported to past major releases. + +## Contribute + +OpenZeppelin Contracts exists thanks to its contributors. There are many ways you can participate and help build high quality software. Check out the [contribution guide](CONTRIBUTING.md)! + +## License + +OpenZeppelin Contracts is released under the [MIT License](LICENSE). diff --git a/lib/openzeppelin-contracts/RELEASING.md b/lib/openzeppelin-contracts/RELEASING.md new file mode 100644 index 0000000..f356ab2 --- /dev/null +++ b/lib/openzeppelin-contracts/RELEASING.md @@ -0,0 +1,36 @@ +# Releasing + +> Visit the documentation for [details about release schedule]. + +Start on an up-to-date `master` branch. + +Create the release branch with `npm run release start minor`. + +Publish a release candidate with `npm run release rc`. + +Publish the final release with `npm run release final`. + +Follow the general [OpenZeppelin Contracts release checklist]. + +[details about release schedule]: https://docs.openzeppelin.com/contracts/releases-stability +[OpenZeppelin Contracts release checklist]: https://github.com/OpenZeppelin/code-style/blob/master/RELEASE_CHECKLIST.md + + +## Merging the release branch + +After the final release, the release branch should be merged back into `master`. This merge must not be squashed because it would lose the tagged release commit. Since the GitHub repo is set up to only allow squashed merges, the merge should be done locally and pushed. + +Make sure to have the latest changes from `upstream` in your local release branch. + +``` +git checkout release-vX.Y.Z +git pull upstream +``` + +``` +git checkout master +git merge --no-ff release-vX.Y.Z +git push upstream master +``` + +The release branch can then be deleted on GitHub. diff --git a/lib/openzeppelin-contracts/SECURITY.md b/lib/openzeppelin-contracts/SECURITY.md new file mode 100644 index 0000000..98701be --- /dev/null +++ b/lib/openzeppelin-contracts/SECURITY.md @@ -0,0 +1,20 @@ +# Security Policy + +## Bug Bounty + +We have a [**bug bounty program** on Immunefi](https://www.immunefi.com/bounty/openzeppelin). Please report any security issues you find through the Immunefi dashboard, or reach out to security@openzeppelin.com. + +Critical bug fixes will be backported to past major releases. + +## Supported Versions + +The recommendation is to use the latest version available. + +| Version | Supported | +| ------- | ------------------------------------ | +| 4.x | :white_check_mark::white_check_mark: | +| 3.4 | :white_check_mark: | +| 2.5 | :white_check_mark: | +| < 2.0 | :x: | + +Note that the Solidity language itself only guarantees security updates for the latest release. diff --git a/lib/openzeppelin-contracts/audit/2017-03.md b/lib/openzeppelin-contracts/audit/2017-03.md new file mode 100644 index 0000000..5ca874b --- /dev/null +++ b/lib/openzeppelin-contracts/audit/2017-03.md @@ -0,0 +1,292 @@ +# OpenZeppelin Audit + +NOTE ON 2021-07-19: This report makes reference to Zeppelin, OpenZeppelin, OpenZeppelin [C]ontracts, the OpenZeppelin team, and OpenZeppelin library. Many of these things have since been renamed and know that this audit applies to what is currently called the OpenZeppelin Contracts which are maintained by the OpenZeppelin Conracts Community. + +March, 2017 +Authored by Dennis Peterson and Peter Vessenes + +# Introduction + +Zeppelin requested that New Alchemy perform an audit of the contracts in their OpenZeppelin library. The OpenZeppelin contracts are a set of contracts intended to be a safe building block for a variety of uses by parties that may not be as sophisticated as the OpenZeppelin team. It is a design goal that the contracts be deployable safely and "as-is". + +The contracts are hosted at: + +https://github.com/OpenZeppelin/zeppelin-solidity + +All the contracts in the "contracts" folder are in scope. + +The git commit hash we evaluated is: +9c5975a706b076b7000e8179f8101e0c61024c87 + +# Disclaimer + +The audit makes no statements or warrantees about utility of the code, safety of the code, suitability of the business model, regulatory regime for the business model, or any other statements about fitness of the contracts to purpose, or their bugfree status. The audit documentation is for discussion purposes only. + +# Executive Summary + +Overall the OpenZeppelin codebase is of reasonably high quality -- it is clean, modular and follows best practices throughout. + +It is still in flux as a codebase, and needs better documentation per file as to expected behavior and future plans. It probably needs more comprehensive and aggressive tests written by people less nice than the current OpenZeppelin team. + +We identified two critical errors and one moderate issue, and would not recommend this commit hash for public use until these bugs are remedied. + +The repository includes a set of Truffle unit tests, a requirement and best practice for smart contracts like these; we recommend these be bulked up. + +# Discussion + +## Big Picture: Is This A Worthwhile Project? + +As soon as a developer touches OpenZeppelin contracts, they will modify something, leaving them in an un-audited state. We do not recommend developers deploy any unaudited code to the Blockchain if it will handle money, information or other things of value. + +> "In accordance with Unix philosophy, Perl gives you enough rope to hang yourself" +> --Larry Wall + +We think this is an incredibly worthwhile project -- aided by the high code quality. Creating a framework that can be easily extended helps increase the average code quality on the Blockchain by charting a course for developers and encouraging containment of modifications to certain sections. + +> "Rust: The language that makes you take the safety off before shooting yourself in the foot" +> -- (@mbrubeck) + +We think much more could be done here, and recommend the OpenZeppelin team keep at this and keep focusing on the design goal of removing rope and adding safety. + +## Solidity Version Updates Recommended + +Most of the code uses Solidity 0.4.11, but some files under `Ownership` are marked 0.4.0. These should be updated. + +Solidity 0.4.10 will add several features which could be useful in these contracts: + +- `assert(condition)`, which throws if the condition is false + +- `revert()`, which rolls back without consuming all remaining gas. + +- `address.transfer(value)`, which is like `send` but automatically propagates exceptions, and supports `.gas()`. See https://github.com/ethereum/solidity/issues/610 for more on this. + +## Error Handling: Throw vs Return False +Solidity standards allow two ways to handle an error -- either calling `throw` or returning `false`. Both have benefits. In particular, a `throw` guarantees a complete wipe of the call stack (up to the preceding external call), whereas `false` allows a function to continue. + +In general we prefer `throw` in our code audits, because it is simpler -- it's less for an engineer to keep track of. Returning `false` and using logic to check results can quickly become a poorly-tracked state machine, and this sort of complexity can cause errors. + +In the OpenZeppelin contracts, both styles are used in different parts of the codebase. `SimpleToken` transfers throw upon failure, while the full ERC20 token returns `false`. Some modifiers `throw`, others just wrap the function body in a conditional, effectively allowing the function to return false if the condition is not met. + +We don't love this, and would usually recommend you stick with one style or the other throughout the codebase. + +In at least one case, these different techniques are combined cleverly (see the Multisig comments, line 65). As a set of contracts intended for general use, we recommend you either strive for more consistency or document explicit design criteria that govern which techniques are used where. + +Note that it may be impossible to use either one in all situations. For example, SafeMath functions pretty much have to throw upon failure, but ERC20 specifies returning booleans. Therefore we make no particular recommendations, but simply point out inconsistencies to consider. + +# Critical Issues + +## Stuck Ether in Crowdsale contract +CrowdsaleToken.sol has no provision for withdrawing the raised ether. We *strongly* recommend a standard `withdraw` function be added. There is no scenario in which someone should deploy this contract as is, whether for testing or live. + +## Recursive Call in MultisigWallet +Line 45 of `MultisigWallet.sol` checks if the amount being sent by `execute` is under a daily limit. + +This function can only be called by the "Owner". As a first angle of attack, it's worth asking what will happen if the multisig wallet owners reset the daily limit by approving a call to `resetSpentToday`. + +If a chain of calls can be constructed in which the owner confirms the `resetSpentToday` function and then withdraws through `execute` in a recursive call, the contract can be drained. In fact, this could be done without a recursive call, just through repeated `execute` calls alternating with the `confirm` calls. + +We are still working through the confirmation protocol in `Shareable.sol`, but we are not convinced that this is impossible, in fact it looks possible. The flexibility any shared owner has in being able to revoke confirmation later is another worrisome angle of approach even if some simple patches are included. + +This bug has a number of causes that need to be addressed: + +1. `resetSpentToday` and `confirm` together do not limit the days on which the function can be called or (it appears) the number of times it can be called. +1. Once a call has been confirmed and `execute`d it appears that it can be re-executed. This is not good. +3. `confirmandCheck` doesn't seem to have logic about whether or not the function in question has been called. +4. Even if it did, `revoke` would need updates and logic to deal with revocation requests after a function call had been completed. + +We do not recommend using the MultisigWallet until these issues are fixed. + +# Moderate to Minor Issues + +## PullPayment +PullPayment.sol needs some work. It has no explicit provision for cancelling a payment. This would be desirable in a number of scenarios; consider a payee losing their wallet, or giving a griefing address, or just an address that requires more than the default gas offered by `send`. + +`asyncSend` has no overflow checking. This is a bad plan. We recommend overflow and underflow checking at the layer closest to the data manipulation. + +`asyncSend` allows more balance to be queued up for sending than the contract holds. This is probably a bad idea, or at the very least should be called something different. If the intent is to allow this, it should have provisions for dealing with race conditions between competing `withdrawPayments` calls. + +It would be nice to see how many payments are pending. This would imply a bit of a rewrite; we recommend this contract get some design time, and that developers don't rely on it in its current state. + +## Shareable Contract + +We do not believe the `Shareable.sol` contract is ready for primetime. It is missing functions, and as written may be vulnerable to a reordering attack -- an attack in which a miner or other party "racing" with a smart contract participant inserts their own information into a list or mapping. + +The confirmation and revocation code needs to be looked over with a very careful eye imagining extraordinarily bad behavior by shared owners before this contract can be called safe. + +No sanity checks on the initial constructor's `required` argument are worrisome as well. + +# Line by Line Comments + +## Lifecycle + +### Killable + +Very simple, allows owner to call selfdestruct, sending funds to owner. No issues. However, note that `selfdestruct` should typically not be used; it is common that a developer may want to access data in a former contract, and they may not understand that `selfdestruct` limits access to the contract. We recommend better documentation about this dynamic, and an alternate function name for `kill` like `completelyDestroy` while `kill` would perhaps merely send funds to the owner. + +Also note that a killable function allows the owner to take funds regardless of other logic. This may be desirable or undesirable depending on the circumstances. Perhaps `Killable` should have a different name as well. + +### Migrations + +I presume that the goal of this contract is to allow and annotate a migration to a new smart contract address. We are not clear here how this would be accomplished by the code; we'd like to review with the OpenZeppelin team. + +### Pausable + +We like these pauses! Note that these allow significant griefing potential by owners, and that this might not be obvious to participants in smart contracts using the OpenZeppelin framework. We would recommend that additional sample logic be added to for instance the TokenContract showing safer use of the pause and resume functions. In particular, we would recommend a timelock after which anyone could unpause the contract. + +The modifiers use the pattern `if(bool){_;}`. This is fine for functions that return false upon failure, but could be problematic for functions expected to throw upon failure. See our comments above on standardizing on `throw` or `return(false)`. + +## Ownership + +### Ownable + +Line 19: Modifier throws if doesn't meet condition, in contrast to some other inheritable modifiers (e.g. in Pausable) that use `if(bool){_;}`. + +### Claimable + +Inherits from Ownable but the existing owner sets a pendingOwner who has to claim ownership. + +Line 17: Another modifier that throws. + +### DelayedClaimable + +Is there any reason to descend from Ownable directly, instead of just Claimable, which descends from Ownable? If not, descending from both just adds confusion. + +### Contactable + +Allows owner to set a public string of contract information. No issues. + +### Shareable + +This needs some work. Doesn't check if `_required <= len(_owners)` for instance, that would be a bummer. What if _required were like `MAX - 1`? + +I have a general concern about the difference between `owners`, `_owners`, and `owner` in `Ownable.sol`. I recommend "Owners" be renamed. In general we do not recomment single character differences in variable names, although a preceding underscore is not uncommon in Solidity code. + +Line 34: "this contract only has six types of events"...actually only two. + +Line 61: Why is `ownerIndex` keyed by addresses hashed to `uint`s? Why not use the addresses directly, so `ownerIndex` is less obscure, and so there's stronger typing? + +Line 62: Do not love `++i) ... owners[2+ i]`. Makes me do math, which is not what I want to do. I want to not have to do math. + +There should probably be a function for adding a new operation, so the developer doesn't have to work directly with the internal data. (This would make the multisig contract even shorter.) + +There's a `revoke` function but not a `propose` function that we can see. + +Beware reordering. If `propose` allows the user to choose a bytes string for their proposal, bad things(TM) will happen as currently written. + + +### Multisig + +Just an interface. Note it allows changing an owner address, but not changing the number of owners. This is somewhat limiting but also simplifies implementation. + +## Payment + +### PullPayment + +Safe from reentrance attack since ether send is at the end, plus it uses `.send()` rather than `.call.value()`. + +There's an argument to be made that `.call.value()` is a better option *if* you're sure that it will be done after all state updates, since `.send` will fail if the recipient has an expensive fallback function. However, in the context of a function meant to be embedded in other contracts, it's probably better to use `.send`. One possible compromise is to add a function which allows only the owner to send ether via `.call.value`. + +If you don't use `call.value` you should implement a `cancel` function in case some value is pending here. + +Line 14: +Doesn't use safeAdd. Although it appears that payout amounts can only be increased, in fact the payer could lower the payout as much as desired via overflow. Also, the payer could add a large non-overflowing amount, causing the payment to exceed the contract balance and therefore fail when withdraw is attempted. + +Recommendation: track the sum of non-withdrawn asyncSends, and don't allow a new one which exceeds the leftover balance. If it's ever desirable to make payments revocable, it should be done explicitly. + +## Tokens + +### ERC20 + +Standard ERC20 interface only. + +There's a security hole in the standard, reported at Edcon: `approve` does not protect against race conditions and simply replaces the current value. An approved spender could wait for the owner to call `approve` again, then attempt to spend the old limit before the new limit is applied. If successful, this attacker could successfully spend the sum of both limits. + +This could be fixed by either (1) including the old limit as a parameter, so the update will fail if some gets spent, or (2) using the value parameter as a delta instead of replacement value. + +This is not fixable while adhering to the current full ERC20 standard, though it would be possible to add a "secureApprove" function. The impact isn't extreme since at least you can only be attacked by addresses you approved. Also, users could mitigate this by always setting spending limits to zero and checking for spends, before setting the new limit. + +Edcon slides: +https://drive.google.com/file/d/0ByMtMw2hul0EN3NCaVFHSFdxRzA/view + +### ERC20Basic + +Simpler interface skipping the Approve function. Note this departs from ERC20 in another way: transfer throws instead of returning false. + +### BasicToken + +Uses `SafeSub` and `SafeMath`, so transfer `throw`s instead of returning false. This complies with ERC20Basic but not the actual ERC20 standard. + +### StandardToken + +Implementation of full ERC20 token. + +Transfer() and transferFrom() use SafeMath functions, which will cause them to throw instead of returning false. Not a security issue but departs from standard. + +### SimpleToken + +Sample instantiation of StandardToken. Note that in this sample, decimals is 18 and supply only 10,000, so the supply is a small fraction of a single nominal token. + +### CrowdsaleToken + +StandardToken which mints tokens at a fixed price when sent ether. + +There's no provision for owner withdrawing the ether. As a sample for crowdsales it should be Ownable and allow the owner to withdraw ether, rather than stranding the ether in the contract. + +Note: an alternative pattern is a mint() function which is only callable from a separate crowdsale contract, so any sort of rules can be added without modifying the token itself. + +### VestedToken + +Lines 23, 27: +Functions `transfer()` and `transferFrom()` have a modifier canTransfer which throws if not enough tokens are available. However, transfer() returns a boolean success. Inconsistent treatment of failure conditions may cause problems for other contracts using the token. (Note that transferableTokens() relies on safeSub(), so will also throw if there's insufficient balance.) + +Line 64: +Delete not actually necessary since the value is overwritten in the next line anyway. + +## Root level + +### Bounty + +Avoids potential race condition by having each researcher deploy a separate contract for attack; if a research manages to break his associated contract, other researchers can't immediately claim the reward, they have to reproduce the attack in their own contracts. + +A developer could subvert this intent by implementing `deployContract()` to always return the same address. However, this would break the `researchers` mapping, updating the researcher address associated with the contract. This could be prevented by blocking rewrites in `researchers`. + +### DayLimit + +The modifier `limitedDaily` calls `underLimit`, which both checks that the spend is below the daily limit, and adds the input value to the daily spend. This is fine if all functions throw upon failure. However, not all OpenZeppelin functions do this; there are functions that returns false, and modifiers that wrap the function body in `if (bool) {_;}`. In these cases, `_value` will be added to `spentToday`, but ether may not actually be sent because other preconditions were not met. (However in the OpenZeppelin multisig this is not a problem.) + +Lines 4, 11: +Comment claims that `DayLimit` is multiowned, and Shareable is imported, but DayLimit does not actually inherit from Shareable. The intent may be for child contracts to inherit from Shareable (as Multisig does); in this case the import should be removed and the comment altered. + +Line 46: +Manual overflow check instead of using safeAdd. Since this is called from a function that throws upon failure anyway, there's no real downside to using safeAdd. + +### LimitBalance + +No issues. + +### MultisigWallet + +Lines 28, 76, 80: +`kill`, `setDailyLimit`, and `resetSpentToday` only happen with multisig approval, and hashes for these actions are logged by Shareable. However, they should probably post their own events for easy reading. + +Line 45: +This call to underLimit will reduce the daily limit, and then either throw or return 0. So in this case there's no danger that the limit will be reduced without the operation going through. + +Line 65: +Shareable's onlyManyOwners will take the user's confirmation, and execute the function body if and only if enough users have confirmed. Whole thing throws if the send fails, which will roll back the confirmation. Confirm returns false if not enough have confirmed yet, true if the whole thing succeeds, and throws only in the exceptional circumstance that the designated transaction unexpectedly fails. Elegant design. + +Line 68: +Throw here is good but note this function can fail either by returning false or by throwing. + +Line 92: +A bit odd to split `clearPending()` between this contract and Shareable. However this does allow contracts inheriting from Shareable to use custom structs for pending transactions. + + +### SafeMath + +Another interesting comment from the same Edcon presentation was that the overflow behavior of Solidity is undocumented, so in theory, source code that relies on it could break with a future revision. + +However, compiled code should be fine, and in the unlikely event that the compiler is revised in this way, there should be plenty of warning. (But this is an argument for keeping overflow checks isolated in SafeMath.) + +Aside from that small caveat, these are fine. + diff --git a/lib/openzeppelin-contracts/audit/2018-10.pdf b/lib/openzeppelin-contracts/audit/2018-10.pdf new file mode 100644 index 0000000..d5bf127 Binary files /dev/null and b/lib/openzeppelin-contracts/audit/2018-10.pdf differ diff --git a/lib/openzeppelin-contracts/certora/Makefile b/lib/openzeppelin-contracts/certora/Makefile new file mode 100644 index 0000000..bbbddbc --- /dev/null +++ b/lib/openzeppelin-contracts/certora/Makefile @@ -0,0 +1,24 @@ +default: help + +PATCH = applyHarness.patch +CONTRACTS_DIR = ../contracts +MUNGED_DIR = munged + +help: + @echo "usage:" + @echo " make clean: remove all generated files (those ignored by git)" + @echo " make $(MUNGED_DIR): create $(MUNGED_DIR) directory by applying the patch file to $(CONTRACTS_DIR)" + @echo " make record: record a new patch file capturing the differences between $(CONTRACTS_DIR) and $(MUNGED_DIR)" + +munged: $(wildcard $(CONTRACTS_DIR)/*.sol) $(PATCH) + rm -rf $@ + cp -r $(CONTRACTS_DIR) $@ + patch -p0 -d $@ < $(PATCH) + +record: + diff -ruN $(CONTRACTS_DIR) $(MUNGED_DIR) | sed 's+../contracts/++g' | sed 's+munged/++g' > $(PATCH) + +clean: + git clean -fdX + touch $(PATCH) + diff --git a/lib/openzeppelin-contracts/certora/README.md b/lib/openzeppelin-contracts/certora/README.md new file mode 100644 index 0000000..55f84d4 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/README.md @@ -0,0 +1,56 @@ +# Running the certora verification tool + +These instructions detail the process for running CVT on the OpenZeppelin (Wizard/Governor) contracts. + +Documentation for CVT and the specification language are available +[here](https://certora.atlassian.net/wiki/spaces/CPD/overview) + +## Running the verification + +The scripts in the `certora/scripts` directory are used to submit verification +jobs to the Certora verification service. After the job is complete, the results will be available on +[the Certora portal](https://vaas-stg.certora.com/). + +These scripts should be run from the root directory; for example by running + +``` +sh certora/scripts/verifyAll.sh +``` + +The most important of these is `verifyAll.sh`, which checks +all of the harnessed contracts (`certora/harness/Wizard*.sol`) against all of +the specifications (`certora/spec/*.spec`). + +The other scripts run a subset of the specifications or the contracts. You can +verify different contracts or specifications by changing the `--verify` option, +and you can run a single rule or method with the `--rule` or `--method` option. + +For example, to verify the `WizardFirstPriority` contract against the +`GovernorCountingSimple` specification, you could change the `--verify` line of +the `WizardControlFirstPriortity.sh` script to: + +``` +--verify WizardFirstPriority:certora/specs/GovernorCountingSimple.spec \ +``` + +## Adapting to changes in the contracts + +Some of our rules require the code to be simplified in various ways. Our +primary tool for performing these simplifications is to run verification on a +contract that extends the original contracts and overrides some of the methods. +These "harness" contracts can be found in the `certora/harness` directory. + +This pattern does require some modifications to the original code: some methods +need to be made virtual or public, for example. These changes are handled by +applying a patch to the code before verification. + +When one of the `verify` scripts is executed, it first applies the patch file +`certora/applyHarness.patch` to the `contracts` directory, placing the output +in the `certora/munged` directory. We then verify the contracts in the +`certora/munged` directory. + +If the original contracts change, it is possible to create a conflict with the +patch. In this case, the verify scripts will report an error message and output +rejected changes in the `munged` directory. After merging the changes, run +`make record` in the `certora` directory; this will regenerate the patch file, +which can then be checked into git. diff --git a/lib/openzeppelin-contracts/certora/applyHarness.patch b/lib/openzeppelin-contracts/certora/applyHarness.patch new file mode 100644 index 0000000..0fbe9ac --- /dev/null +++ b/lib/openzeppelin-contracts/certora/applyHarness.patch @@ -0,0 +1,101 @@ +diff -ruN .gitignore .gitignore +--- .gitignore 1969-12-31 19:00:00.000000000 -0500 ++++ .gitignore 2021-12-09 14:46:33.923637220 -0500 +@@ -0,0 +1,2 @@ ++* ++!.gitignore +diff -ruN governance/compatibility/GovernorCompatibilityBravo.sol governance/compatibility/GovernorCompatibilityBravo.sol +--- governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/compatibility/GovernorCompatibilityBravo.sol 2021-12-09 14:46:33.923637220 -0500 +@@ -245,7 +245,7 @@ + /** + * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. + */ +- function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ++ function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + ProposalDetails storage details = _proposalDetails[proposalId]; + return quorum(proposalSnapshot(proposalId)) <= details.forVotes; + } +@@ -253,7 +253,7 @@ + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be scritly over the againstVotes. + */ +- function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { ++ function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { // HARNESS: changed to public from internal + ProposalDetails storage details = _proposalDetails[proposalId]; + return details.forVotes > details.againstVotes; + } +diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol +--- governance/extensions/GovernorCountingSimple.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/extensions/GovernorCountingSimple.sol 2021-12-09 14:46:33.923637220 -0500 +@@ -64,7 +64,7 @@ + /** + * @dev See {Governor-_quorumReached}. + */ +- function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { ++ function _quorumReached(uint256 proposalId) public view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes; +@@ -73,7 +73,7 @@ + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ +- function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { ++ function _voteSucceeded(uint256 proposalId) public view virtual override returns (bool) { + ProposalVote storage proposalvote = _proposalVotes[proposalId]; + + return proposalvote.forVotes > proposalvote.againstVotes; +diff -ruN governance/extensions/GovernorTimelockControl.sol governance/extensions/GovernorTimelockControl.sol +--- governance/extensions/GovernorTimelockControl.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/extensions/GovernorTimelockControl.sol 2021-12-09 14:46:33.923637220 -0500 +@@ -111,7 +111,7 @@ + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override { +- _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); ++ _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + } + + /** +diff -ruN governance/Governor.sol governance/Governor.sol +--- governance/Governor.sol 2021-12-03 15:24:56.523654357 -0500 ++++ governance/Governor.sol 2021-12-09 14:46:56.411503587 -0500 +@@ -38,8 +38,8 @@ + + string private _name; + +- mapping(uint256 => ProposalCore) private _proposals; +- ++ mapping(uint256 => ProposalCore) public _proposals; ++ + /** + * @dev Restrict access to governor executing address. Some module might override the _executor function to make + * sure this modifier is consistent with the execution model. +@@ -167,12 +167,12 @@ + /** + * @dev Amount of votes already cast passes the threshold limit. + */ +- function _quorumReached(uint256 proposalId) internal view virtual returns (bool); ++ function _quorumReached(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + + /** + * @dev Is the proposal successful or not. + */ +- function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); ++ function _voteSucceeded(uint256 proposalId) public view virtual returns (bool); // HARNESS: changed to public from internal + + /** + * @dev Register a vote with a given support and voting weight. +diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol +--- token/ERC20/extensions/ERC20Votes.sol 2021-12-03 15:24:56.527654330 -0500 ++++ token/ERC20/extensions/ERC20Votes.sol 2021-12-09 14:46:33.927637196 -0500 +@@ -84,7 +84,7 @@ + * + * - `blockNumber` must have been already mined + */ +- function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) { ++ function getPastVotes(address account, uint256 blockNumber) public view virtual returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } diff --git a/lib/openzeppelin-contracts/certora/harnesses/ERC20VotesHarness.sol b/lib/openzeppelin-contracts/certora/harnesses/ERC20VotesHarness.sol new file mode 100644 index 0000000..5067ecf --- /dev/null +++ b/lib/openzeppelin-contracts/certora/harnesses/ERC20VotesHarness.sol @@ -0,0 +1,28 @@ +import "../munged/token/ERC20/extensions/ERC20Votes.sol"; + +contract ERC20VotesHarness is ERC20Votes { + constructor(string memory name, string memory symbol) ERC20Permit(name) ERC20(name, symbol) {} + + mapping(address => mapping(uint256 => uint256)) public _getPastVotes; + + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override { + super._afterTokenTransfer(from, to, amount); + _getPastVotes[from][block.number] -= amount; + _getPastVotes[to][block.number] += amount; + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {DelegateChanged} and {DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual override{ + super._delegate(delegator, delegatee); + _getPastVotes[delegator][block.number] -= balanceOf(delegator); + _getPastVotes[delegatee][block.number] += balanceOf(delegator); + } +} diff --git a/lib/openzeppelin-contracts/certora/harnesses/WizardControlFirstPriority.sol b/lib/openzeppelin-contracts/certora/harnesses/WizardControlFirstPriority.sol new file mode 100644 index 0000000..5ae7fe0 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/harnesses/WizardControlFirstPriority.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../munged/governance/Governor.sol"; +import "../munged/governance/extensions/GovernorCountingSimple.sol"; +import "../munged/governance/extensions/GovernorVotes.sol"; +import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../munged/governance/extensions/GovernorTimelockControl.sol"; +import "../munged/governance/extensions/GovernorProposalThreshold.sol"; + +/* +Wizard options: +ProposalThreshhold = 10 +ERC20Votes +TimelockController +*/ + +contract WizardControlFirstPriority is Governor, GovernorProposalThreshold, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { + constructor(ERC20Votes _token, TimelockController _timelock, string memory name, uint256 quorumFraction) + Governor(name) + GovernorVotes(_token) + GovernorVotesQuorumFraction(quorumFraction) + GovernorTimelockControl(_timelock) + {} + + //HARNESS + + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + + uint256 _votingDelay; + + uint256 _votingPeriod; + + uint256 _proposalThreshold; + + mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; + + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal override virtual returns (uint256) { + + uint256 deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + ghost_sum_vote_power_by_id[proposalId] += deltaWeight; + + return deltaWeight; + } + + function snapshot(uint256 proposalId) public view returns (uint64) { + return _proposals[proposalId].voteStart._deadline; + } + + + function getExecutor() public view returns (address){ + return _executor(); + } + + // original code, harnessed + + function votingDelay() public view override returns (uint256) { // HARNESS: pure -> view + return _votingDelay; // HARNESS: parametric + } + + function votingPeriod() public view override returns (uint256) { // HARNESS: pure -> view + return _votingPeriod; // HARNESS: parametric + } + + function proposalThreshold() public view override returns (uint256) { // HARNESS: pure -> view + return _proposalThreshold; // HARNESS: parametric + } + + // original code, not harnessed + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, GovernorProposalThreshold, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockControl) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/certora/harnesses/WizardFirstTry.sol b/lib/openzeppelin-contracts/certora/harnesses/WizardFirstTry.sol new file mode 100644 index 0000000..83fece0 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/harnesses/WizardFirstTry.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../munged/governance/Governor.sol"; +import "../munged/governance/extensions/GovernorCountingSimple.sol"; +import "../munged/governance/extensions/GovernorVotes.sol"; +import "../munged/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../munged/governance/extensions/GovernorTimelockCompound.sol"; + +/* +Wizard options: +ERC20Votes +TimelockCompound +*/ + +contract WizardFirstTry is Governor, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockCompound { + constructor(ERC20Votes _token, ICompoundTimelock _timelock, string memory name, uint256 quorumFraction) + Governor(name) + GovernorVotes(_token) + GovernorVotesQuorumFraction(quorumFraction) + GovernorTimelockCompound(_timelock) + {} + + //HARNESS + + function isExecuted(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].executed; + } + + function isCanceled(uint256 proposalId) public view returns (bool) { + return _proposals[proposalId].canceled; + } + + function snapshot(uint256 proposalId) public view returns (uint64) { + return _proposals[proposalId].voteStart._deadline; + } + + function getExecutor() public view returns (address){ + return _executor(); + } + + uint256 _votingDelay; + + uint256 _votingPeriod; + + mapping(uint256 => uint256) public ghost_sum_vote_power_by_id; + + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal override virtual returns (uint256) { + + uint256 deltaWeight = super._castVote(proposalId, account, support, reason); //HARNESS + ghost_sum_vote_power_by_id[proposalId] += deltaWeight; + + return deltaWeight; + } + + // original code, harnessed + + function votingDelay() public view override virtual returns (uint256) { // HARNESS: pure -> view + return _votingDelay; // HARNESS: parametric + } + + function votingPeriod() public view override virtual returns (uint256) { // HARNESS: pure -> view + return _votingPeriod; // HARNESS: parametric + } + + // original code, not harnessed + // The following functions are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, GovernorTimelockCompound) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockCompound) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockCompound) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockCompound) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, GovernorTimelockCompound) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/certora/munged/.gitignore b/lib/openzeppelin-contracts/certora/munged/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/munged/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/lib/openzeppelin-contracts/certora/scripts/Governor.sh b/lib/openzeppelin-contracts/certora/scripts/Governor.sh new file mode 100644 index 0000000..53ade50 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/scripts/Governor.sh @@ -0,0 +1,10 @@ +make -C certora munged + +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorHarness.sol \ + --verify GovernorHarness:certora/specs/GovernorBase.spec \ + --solc solc8.0 \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule voteStartBeforeVoteEnd \ + --msg "$1" diff --git a/lib/openzeppelin-contracts/certora/scripts/GovernorCountingSimple-counting.sh b/lib/openzeppelin-contracts/certora/scripts/GovernorCountingSimple-counting.sh new file mode 100644 index 0000000..9ed8fe3 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/scripts/GovernorCountingSimple-counting.sh @@ -0,0 +1,10 @@ +make -C certora munged + +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/GovernorBasicHarness.sol \ + --verify GovernorBasicHarness:certora/specs/GovernorCountingSimple.spec \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule hasVotedCorrelation \ + --msg "$1" diff --git a/lib/openzeppelin-contracts/certora/scripts/WizardControlFirstPriority.sh b/lib/openzeppelin-contracts/certora/scripts/WizardControlFirstPriority.sh new file mode 100644 index 0000000..b815986 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/scripts/WizardControlFirstPriority.sh @@ -0,0 +1,12 @@ +make -C certora munged + +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardControlFirstPriority.sol \ + --link WizardControlFirstPriority:token=ERC20VotesHarness \ + --verify WizardControlFirstPriority:certora/specs/GovernorBase.spec \ + --solc solc8.2 \ + --disableLocalTypeChecking \ + --staging shelly/forSasha \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --rule canVoteDuringVotingPeriod \ + --msg "$1" diff --git a/lib/openzeppelin-contracts/certora/scripts/WizardFirstTry.sh b/lib/openzeppelin-contracts/certora/scripts/WizardFirstTry.sh new file mode 100644 index 0000000..fd5a32a --- /dev/null +++ b/lib/openzeppelin-contracts/certora/scripts/WizardFirstTry.sh @@ -0,0 +1,10 @@ +make -C certora munged + +certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/WizardFirstTry.sol \ + --verify WizardFirstTry:certora/specs/GovernorBase.spec \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --optimistic_loop \ + --disableLocalTypeChecking \ + --settings -copyLoopUnroll=4 \ + --msg "$1" diff --git a/lib/openzeppelin-contracts/certora/scripts/sanity.sh b/lib/openzeppelin-contracts/certora/scripts/sanity.sh new file mode 100644 index 0000000..b6cdb4e --- /dev/null +++ b/lib/openzeppelin-contracts/certora/scripts/sanity.sh @@ -0,0 +1,14 @@ +make -C certora munged + +for f in certora/harnesses/Wizard*.sol +do + echo "Processing $f" + file=$(basename $f) + echo ${file%.*} + certoraRun certora/harnesses/$file \ + --verify ${file%.*}:certora/specs/sanity.spec "$@" \ + --solc solc8.2 --staging shelly/forSasha \ + --optimistic_loop \ + --msg "checking sanity on ${file%.*}" + --settings -copyLoopUnroll=4 +done diff --git a/lib/openzeppelin-contracts/certora/scripts/verifyAll.sh b/lib/openzeppelin-contracts/certora/scripts/verifyAll.sh new file mode 100644 index 0000000..90d7691 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/scripts/verifyAll.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +make -C certora munged + +for contract in certora/harnesses/Wizard*.sol; +do + for spec in certora/specs/*.spec; + do + contractFile=$(basename $contract) + specFile=$(basename $spec) + if [[ "${specFile%.*}" != "RulesInProgress" ]]; + then + echo "Processing ${contractFile%.*} with $specFile" + if [[ "${contractFile%.*}" = *"WizardControl"* ]]; + then + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --link ${contractFile%.*}:token=ERC20VotesHarness \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --send_only \ + --msg "checking $specFile on ${contractFile%.*}" + else + certoraRun certora/harnesses/ERC20VotesHarness.sol certora/harnesses/$contractFile \ + --verify ${contractFile%.*}:certora/specs/$specFile "$@" \ + --solc solc8.2 \ + --staging shelly/forSasha \ + --disableLocalTypeChecking \ + --optimistic_loop \ + --settings -copyLoopUnroll=4 \ + --send_only \ + --msg "checking $specFile on ${contractFile%.*}" + fi + fi + done +done diff --git a/lib/openzeppelin-contracts/certora/specs/GovernorBase.spec b/lib/openzeppelin-contracts/certora/specs/GovernorBase.spec new file mode 100644 index 0000000..de728dd --- /dev/null +++ b/lib/openzeppelin-contracts/certora/specs/GovernorBase.spec @@ -0,0 +1,333 @@ +////////////////////////////////////////////////////////////////////////////// +///////////////////// Governor.sol base definitions ////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +using ERC20VotesHarness as erc20votes + +methods { + proposalSnapshot(uint256) returns uint256 envfree // matches proposalVoteStart + proposalDeadline(uint256) returns uint256 envfree // matches proposalVoteEnd + hashProposal(address[],uint256[],bytes[],bytes32) returns uint256 envfree + isExecuted(uint256) returns bool envfree + isCanceled(uint256) returns bool envfree + execute(address[], uint256[], bytes[], bytes32) returns uint256 + hasVoted(uint256, address) returns bool + castVote(uint256, uint8) returns uint256 + updateQuorumNumerator(uint256) + queue(address[], uint256[], bytes[], bytes32) returns uint256 + + // internal functions made public in harness: + _quorumReached(uint256) returns bool + _voteSucceeded(uint256) returns bool envfree + + // function summarization + proposalThreshold() returns uint256 envfree + + getVotes(address, uint256) returns uint256 => DISPATCHER(true) + + getPastTotalSupply(uint256 t) returns uint256 => PER_CALLEE_CONSTANT + getPastVotes(address a, uint256 t) returns uint256 => PER_CALLEE_CONSTANT + + //scheduleBatch(address[],uint256[],bytes[],bytes32,bytes32,uint256) => DISPATCHER(true) + //executeBatch(address[], uint256[], bytes[], bytes32, bytes32) => DISPATCHER(true) +} + +////////////////////////////////////////////////////////////////////////////// +//////////////////////////////// Definitions ///////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +// proposal was created - relation proved in noStartBeforeCreation +definition proposalCreated(uint256 pId) returns bool = proposalSnapshot(pId) > 0; + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Helper Functions /////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +function helperFunctionsWithRevert(uint256 proposalId, method f, env e) { + address[] targets; uint256[] values; bytes[] calldatas; string reason; bytes32 descriptionHash; + uint8 support; uint8 v; bytes32 r; bytes32 s; + if (f.selector == propose(address[], uint256[], bytes[], string).selector) { + uint256 result = propose@withrevert(e, targets, values, calldatas, reason); + require(result == proposalId); + } else if (f.selector == execute(address[], uint256[], bytes[], bytes32).selector) { + uint256 result = execute@withrevert(e, targets, values, calldatas, descriptionHash); + require(result == proposalId); + } else if (f.selector == castVote(uint256, uint8).selector) { + castVote@withrevert(e, proposalId, support); + } else if (f.selector == castVoteWithReason(uint256, uint8, string).selector) { + castVoteWithReason@withrevert(e, proposalId, support, reason); + } else if (f.selector == castVoteBySig(uint256, uint8,uint8, bytes32, bytes32).selector) { + castVoteBySig@withrevert(e, proposalId, support, v, r, s); + } else if (f.selector == queue(address[], uint256[], bytes[], bytes32).selector) { + queue@withrevert(e, targets, values, calldatas, descriptionHash); + } else { + calldataarg args; + f@withrevert(e, args); + } +} + +/* + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////// State Diagram ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // // + // castVote(s)() // + // ------------- propose() ---------------------- time pass --------------- time passes ----------- // + // | No Proposal | --------> | Before Start (Delay) | --------> | Voting Period | ----------------------> | execute() | // + // ------------- ---------------------- --------------- -> Executed/Canceled ----------- // + // ------------------------------------------------------------|---------------|-------------------------|--------------> // + // t start end timelock // + // // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +*/ + + +/////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// Global Valid States ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + + +/* + * Start and end date are either initialized (non zero) or uninitialized (zero) simultaneously + * This invariant assumes that the block number cannot be 0 at any stage of the contract cycle + * This is very safe assumption as usually the 0 block is genesis block which is uploaded with data + * by the developers and will not be valid to raise proposals (at the current way that block chain is functioning) + */ + // To use env with general preserved block disable type checking [--disableLocalTypeChecking] +invariant startAndEndDatesNonZero(uint256 pId) + proposalSnapshot(pId) != 0 <=> proposalDeadline(pId) != 0 + { preserved with (env e){ + require e.block.number > 0; + }} + + +/* + * If a proposal is canceled it must have a start and an end date + */ + // To use env with general preserved block disable type checking [--disableLocalTypeChecking] +invariant canceledImplyStartAndEndDateNonZero(uint pId) + isCanceled(pId) => proposalSnapshot(pId) != 0 + {preserved with (env e){ + require e.block.number > 0; + }} + + +/* + * If a proposal is executed it must have a start and an end date + */ + // To use env with general preserved block disable type checking [--disableLocalTypeChecking] +invariant executedImplyStartAndEndDateNonZero(uint pId) + isExecuted(pId) => proposalSnapshot(pId) != 0 + { preserved with (env e){ + requireInvariant startAndEndDatesNonZero(pId); + require e.block.number > 0; + }} + + +/* + * A proposal starting block number must be less or equal than the proposal end date + */ +invariant voteStartBeforeVoteEnd(uint256 pId) + // from < to <= because snapshot and deadline can be the same block number if delays are set to 0 + // This is possible before the integration of GovernorSettings.sol to the system. + // After integration of GovernorSettings.sol the invariant expression should be changed from <= to < + (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) + // (proposalSnapshot(pId) > 0 => proposalSnapshot(pId) <= proposalDeadline(pId)) + { preserved { + requireInvariant startAndEndDatesNonZero(pId); + }} + + +/* + * A proposal cannot be both executed and canceled simultaneously. + */ +invariant noBothExecutedAndCanceled(uint256 pId) + !isExecuted(pId) || !isCanceled(pId) + + +/* + * A proposal could be executed only if quorum was reached and vote succeeded + */ +rule executionOnlyIfQuoromReachedAndVoteSucceeded(uint256 pId, env e, method f){ + bool isExecutedBefore = isExecuted(pId); + bool quorumReachedBefore = _quorumReached(e, pId); + bool voteSucceededBefore = _voteSucceeded(pId); + + calldataarg args; + f(e, args); + + bool isExecutedAfter = isExecuted(pId); + assert (!isExecutedBefore && isExecutedAfter) => (quorumReachedBefore && voteSucceededBefore), "quorum was changed"; +} + +/////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////// In-State Rules ///////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +//========================================== +//------------- Voting Period -------------- +//========================================== + +/* + * A user cannot vote twice + */ + // Checked for castVote only. all 3 castVote functions call _castVote, so the completeness of the verification is counted on + // the fact that the 3 functions themselves makes no changes, but rather call an internal function to execute. + // That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial + // to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it. + // We could check each function separately and pass the rule, but that would have uglyfied the code with no concrete + // benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification. +rule doubleVoting(uint256 pId, uint8 sup, method f) { + env e; + address user = e.msg.sender; + bool votedCheck = hasVoted(e, pId, user); + + castVote@withrevert(e, pId, sup); + + assert votedCheck => lastReverted, "double voting occurred"; +} + + +/////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// State Transitions Rules ////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +//=========================================== +//-------- Propose() --> End of Time -------- +//=========================================== + + +/* + * Once a proposal is created, voteStart and voteEnd are immutable + */ +rule immutableFieldsAfterProposalCreation(uint256 pId, method f) { + uint256 _voteStart = proposalSnapshot(pId); + uint256 _voteEnd = proposalDeadline(pId); + + require proposalCreated(pId); // startDate > 0 + + env e; calldataarg arg; + f(e, arg); + + uint256 voteStart_ = proposalSnapshot(pId); + uint256 voteEnd_ = proposalDeadline(pId); + assert _voteStart == voteStart_, "Start date was changed"; + assert _voteEnd == voteEnd_, "End date was changed"; +} + + +/* + * Voting cannot start at a block number prior to proposal’s creation block number + */ +rule noStartBeforeCreation(uint256 pId) { + uint256 previousStart = proposalSnapshot(pId); + // This line makes sure that we see only cases where start date is changed from 0, i.e. creation of proposal + // We proved in immutableFieldsAfterProposalCreation that once dates set for proposal, it cannot be changed + require !proposalCreated(pId); // previousStart == 0; + + env e; calldataarg args; + propose(e, args); + + uint256 newStart = proposalSnapshot(pId); + // if created, start is after current block number (creation block) + assert(newStart != previousStart => newStart >= e.block.number); +} + + +//============================================ +//--- End of Voting Period --> End of Time --- +//============================================ + + +/* + * A proposal can neither be executed nor canceled before it ends + */ + // By induction it cannot be executed nor canceled before it starts, due to voteStartBeforeVoteEnd +rule noExecuteOrCancelBeforeDeadline(uint256 pId, method f){ + require !isExecuted(pId) && !isCanceled(pId); + + env e; calldataarg args; + f(e, args); + + assert e.block.number < proposalDeadline(pId) => (!isExecuted(pId) && !isCanceled(pId)), "executed/cancelled before deadline"; +} + +//////////////////////////////////////////////////////////////////////////////// +////////////////////// Integrity Of Functions (Unit Tests) ///////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +////////////////////////////// High Level Rules //////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Not Categorized Yet ////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +/* + * All proposal specific (non-view) functions should revert if proposal is executed + */ + // In this rule we show that if a function is executed, i.e. execute() was called on the proposal ID, + // non of the proposal specific functions can make changes again. In executedOnlyAfterExecuteFunc + // we connected the executed attribute to the execute() function, showing that only execute() can + // change it, and that it will always change it. +rule allFunctionsRevertIfExecuted(method f) filtered { f -> + !f.isView && !f.isFallback + && f.selector != updateTimelock(address).selector + && f.selector != updateQuorumNumerator(uint256).selector + && f.selector != queue(address[],uint256[],bytes[],bytes32).selector + && f.selector != relay(address,uint256,bytes).selector + && f.selector != 0xb9a61961 // __acceptAdmin() +} { + env e; calldataarg args; + uint256 pId; + require(isExecuted(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + requireInvariant executedImplyStartAndEndDateNonZero(pId); + + helperFunctionsWithRevert(pId, f, e); + + assert(lastReverted, "Function was not reverted"); +} + +/* + * All proposal specific (non-view) functions should revert if proposal is canceled + */ +rule allFunctionsRevertIfCanceled(method f) filtered { + f -> !f.isView && !f.isFallback + && f.selector != updateTimelock(address).selector + && f.selector != updateQuorumNumerator(uint256).selector + && f.selector != queue(address[],uint256[],bytes[],bytes32).selector + && f.selector != relay(address,uint256,bytes).selector + && f.selector != 0xb9a61961 // __acceptAdmin() +} { + env e; calldataarg args; + uint256 pId; + require(isCanceled(pId)); + requireInvariant noBothExecutedAndCanceled(pId); + requireInvariant canceledImplyStartAndEndDateNonZero(pId); + + helperFunctionsWithRevert(pId, f, e); + + assert(lastReverted, "Function was not reverted"); +} + +/* + * Proposal can be switched to executed only via execute() function + */ +rule executedOnlyAfterExecuteFunc(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash, method f) { + env e; calldataarg args; + uint256 pId; + bool executedBefore = isExecuted(pId); + require(!executedBefore); + + helperFunctionsWithRevert(pId, f, e); + + bool executedAfter = isExecuted(pId); + assert(executedAfter != executedBefore => f.selector == execute(address[], uint256[], bytes[], bytes32).selector, "isExecuted only changes in the execute method"); +} diff --git a/lib/openzeppelin-contracts/certora/specs/GovernorCountingSimple.spec b/lib/openzeppelin-contracts/certora/specs/GovernorCountingSimple.spec new file mode 100644 index 0000000..7af73be --- /dev/null +++ b/lib/openzeppelin-contracts/certora/specs/GovernorCountingSimple.spec @@ -0,0 +1,221 @@ +import "GovernorBase.spec" + +using ERC20VotesHarness as erc20votes + +methods { + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree + + quorum(uint256) returns uint256 + proposalVotes(uint256) returns (uint256, uint256, uint256) envfree + + quorumNumerator() returns uint256 + _executor() returns address + + erc20votes._getPastVotes(address, uint256) returns uint256 + + getExecutor() returns address + + timelock() returns address +} + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +//////////// ghosts to keep track of votes counting //////////// + +/* + * the sum of voting power of those who voted + */ +ghost sum_all_votes_power() returns uint256 { + init_state axiom sum_all_votes_power() == 0; +} + +hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +} + +/* + * sum of all votes casted per proposal + */ +ghost tracked_weight(uint256) returns uint256 { + init_state axiom forall uint256 p. tracked_weight(p) == 0; +} + +/* + * sum of all votes casted + */ +ghost sum_tracked_weight() returns uint256 { + init_state axiom sum_tracked_weight() == 0; +} + +/* + * getter for _proposalVotes.againstVotes + */ +ghost votesAgainst() returns uint256 { + init_state axiom votesAgainst() == 0; +} + +/* + * getter for _proposalVotes.forVotes + */ +ghost votesFor() returns uint256 { + init_state axiom votesFor() == 0; +} + +/* + * getter for _proposalVotes.abstainVotes + */ +ghost votesAbstain() returns uint256 { + init_state axiom votesAbstain() == 0; +} + +hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; +} + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +/* + * sum of all votes casted is equal to the sum of voting power of those who voted, per each proposal + */ +invariant SumOfVotesCastEqualSumOfPowerOfVotedPerProposal(uint256 pId) + tracked_weight(pId) == ghost_sum_vote_power_by_id(pId) + + +/* + * sum of all votes casted is equal to the sum of voting power of those who voted + */ +invariant SumOfVotesCastEqualSumOfPowerOfVoted() + sum_tracked_weight() == sum_all_votes_power() + + +/* +* sum of all votes casted is greater or equal to the sum of voting power of those who voted at a specific proposal +*/ +invariant OneIsNotMoreThanAll(uint256 pId) + sum_all_votes_power() >= tracked_weight(pId) + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// RULES ////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +/* + * Only sender's voting status can be changed by execution of any cast vote function + */ +// Checked for castVote only. all 3 castVote functions call _castVote, so the completeness of the verification is counted on + // the fact that the 3 functions themselves makes no changes, but rather call an internal function to execute. + // That means that we do not check those 3 functions directly, however for castVote & castVoteWithReason it is quite trivial + // to understand why this is ok. For castVoteBySig we basically assume that the signature referendum is correct without checking it. + // We could check each function separately and pass the rule, but that would have uglyfied the code with no concrete + // benefit, as it is evident that nothing is happening in the first 2 functions (calling a view function), and we do not desire to check the signature verification. +rule noVoteForSomeoneElse(uint256 pId, uint8 sup, method f) { + env e; calldataarg args; + + address voter = e.msg.sender; + address user; + + bool hasVotedBefore_User = hasVoted(e, pId, user); + + castVote@withrevert(e, pId, sup); + require(!lastReverted); + + bool hasVotedAfter_User = hasVoted(e, pId, user); + + assert user != voter => hasVotedBefore_User == hasVotedAfter_User; +} + + +/* +* Total voting tally is monotonically non-decreasing in every operation +*/ +rule votingWeightMonotonicity(method f){ + uint256 votingWeightBefore = sum_tracked_weight(); + + env e; + calldataarg args; + f(e, args); + + uint256 votingWeightAfter = sum_tracked_weight(); + + assert votingWeightBefore <= votingWeightAfter, "Voting weight was decreased somehow"; +} + + +/* +* A change in hasVoted must be correlated with an non-decreasing of the vote supports (nondecrease because user can vote with weight 0) +*/ +rule hasVotedCorrelation(uint256 pId, method f, env e, uint256 bn) { + address acc = e.msg.sender; + + uint256 againstBefore = votesAgainst(); + uint256 forBefore = votesFor(); + uint256 abstainBefore = votesAbstain(); + + bool hasVotedBefore = hasVoted(e, pId, acc); + + helperFunctionsWithRevert(pId, f, e); + require(!lastReverted); + + uint256 againstAfter = votesAgainst(); + uint256 forAfter = votesFor(); + uint256 abstainAfter = votesAbstain(); + + bool hasVotedAfter = hasVoted(e, pId, acc); + + assert (!hasVotedBefore && hasVotedAfter) => againstBefore <= againstAfter || forBefore <= forAfter || abstainBefore <= abstainAfter, "no correlation"; +} + + +/* +* Only privileged users can execute privileged operations, e.g. change _quorumNumerator or _timelock +*/ +rule privilegedOnlyNumerator(method f, uint256 newQuorumNumerator){ + env e; + calldataarg arg; + uint256 quorumNumBefore = quorumNumerator(e); + + f(e, arg); + + uint256 quorumNumAfter = quorumNumerator(e); + address executorCheck = getExecutor(e); + + assert quorumNumBefore != quorumNumAfter => e.msg.sender == executorCheck, "non privileged user changed quorum numerator"; +} + +rule privilegedOnlyTimelock(method f, uint256 newQuorumNumerator){ + env e; + calldataarg arg; + uint256 timelockBefore = timelock(e); + + f(e, arg); + + uint256 timelockAfter = timelock(e); + + assert timelockBefore != timelockAfter => e.msg.sender == timelockBefore, "non privileged user changed timelock"; +} diff --git a/lib/openzeppelin-contracts/certora/specs/RulesInProgress.spec b/lib/openzeppelin-contracts/certora/specs/RulesInProgress.spec new file mode 100644 index 0000000..cbad333 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/specs/RulesInProgress.spec @@ -0,0 +1,139 @@ +////////////////////////////////////////////////////////////////////////////// +////////////// THIS SPEC IS A RESERVE FOR NOT IN PROGRESS ////////////// +////////////////////////////////////////////////////////////////////////////// + +import "GovernorBase.spec" + +using ERC20VotesHarness as erc20votes + +methods { + ghost_sum_vote_power_by_id(uint256) returns uint256 envfree + + quorum(uint256) returns uint256 + proposalVotes(uint256) returns (uint256, uint256, uint256) envfree + + quorumNumerator() returns uint256 + _executor() returns address + + erc20votes._getPastVotes(address, uint256) returns uint256 + + getExecutor() returns address + + timelock() returns address +} + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// GHOSTS ///////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +//////////// ghosts to keep track of votes counting //////////// + +/* + * the sum of voting power of those who voted + */ +ghost sum_all_votes_power() returns uint256 { + init_state axiom sum_all_votes_power() == 0; +} + +hook Sstore ghost_sum_vote_power_by_id [KEY uint256 pId] uint256 current_power(uint256 old_power) STORAGE { + havoc sum_all_votes_power assuming sum_all_votes_power@new() == sum_all_votes_power@old() - old_power + current_power; +} + +/* + * sum of all votes casted per proposal + */ +ghost tracked_weight(uint256) returns uint256 { + init_state axiom forall uint256 p. tracked_weight(p) == 0; +} + +/* + * sum of all votes casted + */ +ghost sum_tracked_weight() returns uint256 { + init_state axiom sum_tracked_weight() == 0; +} + +/* + * getter for _proposalVotes.againstVotes + */ +ghost votesAgainst() returns uint256 { + init_state axiom votesAgainst() == 0; +} + +/* + * getter for _proposalVotes.forVotes + */ +ghost votesFor() returns uint256 { + init_state axiom votesFor() == 0; +} + +/* + * getter for _proposalVotes.abstainVotes + */ +ghost votesAbstain() returns uint256 { + init_state axiom votesAbstain() == 0; +} + +hook Sstore _proposalVotes [KEY uint256 pId].againstVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAgainst assuming votesAgainst@new() == votesAgainst@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].forVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesFor assuming votesFor@new() == votesFor@old() - old_votes + votes; +} + +hook Sstore _proposalVotes [KEY uint256 pId].abstainVotes uint256 votes(uint256 old_votes) STORAGE { + havoc tracked_weight assuming forall uint256 p.(p == pId => tracked_weight@new(p) == tracked_weight@old(p) - old_votes + votes) && + (p != pId => tracked_weight@new(p) == tracked_weight@old(p)); + havoc sum_tracked_weight assuming sum_tracked_weight@new() == sum_tracked_weight@old() - old_votes + votes; + havoc votesAbstain assuming votesAbstain@new() == votesAbstain@old() - old_votes + votes; +} + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////// INVARIANTS //////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////// +///////////////////////////////// RULES ////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + + +//NOT FINISHED +/* +* the sum of voting power of those who voted is less or equal to the maximum possible votes, per each proposal +*/ +rule possibleTotalVotes(uint256 pId, uint8 sup, env e, method f) { + + // add requireinvariant for all i, j. i = i - 1 && i < j => checkpointlookup[i] < checkpointlookup[j]; + require tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)); + + uint256 againstB; + uint256 forB; + uint256 absatinB; + againstB, forB, absatinB = proposalVotes(pId); + + calldataarg args; + //f(e, args); + + castVote(e, pId, sup); + + uint256 against; + uint256 for; + uint256 absatin; + against, for, absatin = proposalVotes(pId); + + uint256 ps = proposalSnapshot(pId); + + assert tracked_weight(pId) <= erc20votes.getPastTotalSupply(e, proposalSnapshot(pId)), "bla bla bla"; +} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/certora/specs/sanity.spec b/lib/openzeppelin-contracts/certora/specs/sanity.spec new file mode 100644 index 0000000..e08f688 --- /dev/null +++ b/lib/openzeppelin-contracts/certora/specs/sanity.spec @@ -0,0 +1,14 @@ +/* +This rule looks for a non-reverting execution path to each method, including those overridden in the harness. +A method has such an execution path if it violates this rule. +How it works: + - If there is a non-reverting execution path, we reach the false assertion, and the sanity fails. + - If all execution paths are reverting, we never call the assertion, and the method will pass this rule vacuously. +*/ + +rule sanity(method f) { + env e; + calldataarg arg; + f(e, arg); + assert false; +} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/contracts/access/AccessControl.sol b/lib/openzeppelin-contracts/contracts/access/AccessControl.sol new file mode 100644 index 0000000..386b85c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/AccessControl.sol @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; +import "../utils/Context.sol"; +import "../utils/Strings.sol"; +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Contract module that allows children to implement role-based access + * control mechanisms. This is a lightweight version that doesn't allow enumerating role + * members except through off-chain means by accessing the contract event logs. Some + * applications may benefit from on-chain enumerability, for those cases see + * {AccessControlEnumerable}. + * + * Roles are referred to by their `bytes32` identifier. These should be exposed + * in the external API and be unique. The best way to achieve this is by + * using `public constant` hash digests: + * + * ``` + * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); + * ``` + * + * Roles can be used to represent a set of permissions. To restrict access to a + * function call, use {hasRole}: + * + * ``` + * function foo() public { + * require(hasRole(MY_ROLE, msg.sender)); + * ... + * } + * ``` + * + * Roles can be granted and revoked dynamically via the {grantRole} and + * {revokeRole} functions. Each role has an associated admin role, and only + * accounts that have a role's admin role can call {grantRole} and {revokeRole}. + * + * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means + * that only accounts with this role will be able to grant or revoke other + * roles. More complex role relationships can be created by using + * {_setRoleAdmin}. + * + * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to + * grant and revoke this role. Extra precautions should be taken to secure + * accounts that have been granted it. + */ +abstract contract AccessControl is Context, IAccessControl, ERC165 { + struct RoleData { + mapping(address => bool) members; + bytes32 adminRole; + } + + mapping(bytes32 => RoleData) private _roles; + + bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; + + /** + * @dev Modifier that checks that an account has a specific role. Reverts + * with a standardized message including the required role. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + * + * _Available since v4.1._ + */ + modifier onlyRole(bytes32 role) { + _checkRole(role); + _; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) public view virtual override returns (bool) { + return _roles[role].members[account]; + } + + /** + * @dev Revert with a standard message if `_msgSender()` is missing `role`. + * Overriding this function changes the behavior of the {onlyRole} modifier. + * + * Format of the revert message is described in {_checkRole}. + * + * _Available since v4.6._ + */ + function _checkRole(bytes32 role) internal view virtual { + _checkRole(role, _msgSender()); + } + + /** + * @dev Revert with a standard message if `account` is missing `role`. + * + * The format of the revert reason is given by the following regular expression: + * + * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ + */ + function _checkRole(bytes32 role, address account) internal view virtual { + if (!hasRole(role, account)) { + revert( + string( + abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(account), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ) + ); + } + } + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { + return _roles[role].adminRole; + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + * + * May emit a {RoleGranted} event. + */ + function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _grantRole(role, account); + } + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + * + * May emit a {RoleRevoked} event. + */ + function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { + _revokeRole(role, account); + } + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been revoked `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + * + * May emit a {RoleRevoked} event. + */ + function renounceRole(bytes32 role, address account) public virtual override { + require(account == _msgSender(), "AccessControl: can only renounce roles for self"); + + _revokeRole(role, account); + } + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. Note that unlike {grantRole}, this function doesn't perform any + * checks on the calling account. + * + * May emit a {RoleGranted} event. + * + * [WARNING] + * ==== + * This function should only be called from the constructor when setting + * up the initial roles for the system. + * + * Using this function in any other way is effectively circumventing the admin + * system imposed by {AccessControl}. + * ==== + * + * NOTE: This function is deprecated in favor of {_grantRole}. + */ + function _setupRole(bytes32 role, address account) internal virtual { + _grantRole(role, account); + } + + /** + * @dev Sets `adminRole` as ``role``'s admin role. + * + * Emits a {RoleAdminChanged} event. + */ + function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { + bytes32 previousAdminRole = getRoleAdmin(role); + _roles[role].adminRole = adminRole; + emit RoleAdminChanged(role, previousAdminRole, adminRole); + } + + /** + * @dev Grants `role` to `account`. + * + * Internal function without access restriction. + * + * May emit a {RoleGranted} event. + */ + function _grantRole(bytes32 role, address account) internal virtual { + if (!hasRole(role, account)) { + _roles[role].members[account] = true; + emit RoleGranted(role, account, _msgSender()); + } + } + + /** + * @dev Revokes `role` from `account`. + * + * Internal function without access restriction. + * + * May emit a {RoleRevoked} event. + */ + function _revokeRole(bytes32 role, address account) internal virtual { + if (hasRole(role, account)) { + _roles[role].members[account] = false; + emit RoleRevoked(role, account, _msgSender()); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/access/AccessControlCrossChain.sol b/lib/openzeppelin-contracts/contracts/access/AccessControlCrossChain.sol new file mode 100644 index 0000000..95be509 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/AccessControlCrossChain.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (access/AccessControlCrossChain.sol) + +pragma solidity ^0.8.4; + +import "./AccessControl.sol"; +import "../crosschain/CrossChainEnabled.sol"; + +/** + * @dev An extension to {AccessControl} with support for cross-chain access management. + * For each role, is extension implements an equivalent "aliased" role that is used for + * restricting calls originating from other chains. + * + * For example, if a function `myFunction` is protected by `onlyRole(SOME_ROLE)`, and + * if an address `x` has role `SOME_ROLE`, it would be able to call `myFunction` directly. + * A wallet or contract at the same address on another chain would however not be able + * to call this function. In order to do so, it would require to have the role + * `_crossChainRoleAlias(SOME_ROLE)`. + * + * This aliasing is required to protect against multiple contracts living at the same + * address on different chains but controlled by conflicting entities. + * + * _Available since v4.6._ + */ +abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled { + bytes32 public constant CROSSCHAIN_ALIAS = keccak256("CROSSCHAIN_ALIAS"); + + /** + * @dev See {AccessControl-_checkRole}. + */ + function _checkRole(bytes32 role) internal view virtual override { + if (_isCrossChain()) { + _checkRole(_crossChainRoleAlias(role), _crossChainSender()); + } else { + super._checkRole(role); + } + } + + /** + * @dev Returns the aliased role corresponding to `role`. + */ + function _crossChainRoleAlias(bytes32 role) internal pure virtual returns (bytes32) { + return role ^ CROSSCHAIN_ALIAS; + } +} diff --git a/lib/openzeppelin-contracts/contracts/access/AccessControlEnumerable.sol b/lib/openzeppelin-contracts/contracts/access/AccessControlEnumerable.sol new file mode 100644 index 0000000..354e1be --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/AccessControlEnumerable.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControlEnumerable.sol"; +import "./AccessControl.sol"; +import "../utils/structs/EnumerableSet.sol"; + +/** + * @dev Extension of {AccessControl} that allows enumerating the members of each role. + */ +abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl { + using EnumerableSet for EnumerableSet.AddressSet; + + mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns one of the accounts that have `role`. `index` must be a + * value between 0 and {getRoleMemberCount}, non-inclusive. + * + * Role bearers are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure + * you perform all queries on the same block. See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] + * for more information. + */ + function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) { + return _roleMembers[role].at(index); + } + + /** + * @dev Returns the number of accounts that have `role`. Can be used + * together with {getRoleMember} to enumerate all bearers of a role. + */ + function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) { + return _roleMembers[role].length(); + } + + /** + * @dev Overload {_grantRole} to track enumerable memberships + */ + function _grantRole(bytes32 role, address account) internal virtual override { + super._grantRole(role, account); + _roleMembers[role].add(account); + } + + /** + * @dev Overload {_revokeRole} to track enumerable memberships + */ + function _revokeRole(bytes32 role, address account) internal virtual override { + super._revokeRole(role, account); + _roleMembers[role].remove(account); + } +} diff --git a/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol b/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol new file mode 100644 index 0000000..f773ecc --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) + +pragma solidity ^0.8.0; + +/** + * @dev External interface of AccessControl declared to support ERC165 detection. + */ +interface IAccessControl { + /** + * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` + * + * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite + * {RoleAdminChanged} not being emitted signaling this. + * + * _Available since v3.1._ + */ + event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); + + /** + * @dev Emitted when `account` is granted `role`. + * + * `sender` is the account that originated the contract call, an admin role + * bearer except when using {AccessControl-_setupRole}. + */ + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Emitted when `account` is revoked `role`. + * + * `sender` is the account that originated the contract call: + * - if using `revokeRole`, it is the admin role bearer + * - if using `renounceRole`, it is the role bearer (i.e. `account`) + */ + event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @dev Returns the admin role that controls `role`. See {grantRole} and + * {revokeRole}. + * + * To change a role's admin, use {AccessControl-_setRoleAdmin}. + */ + function getRoleAdmin(bytes32 role) external view returns (bytes32); + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) external; + + /** + * @dev Revokes `role` from the calling account. + * + * Roles are often managed via {grantRole} and {revokeRole}: this function's + * purpose is to provide a mechanism for accounts to lose their privileges + * if they are compromised (such as when a trusted device is misplaced). + * + * If the calling account had been granted `role`, emits a {RoleRevoked} + * event. + * + * Requirements: + * + * - the caller must be `account`. + */ + function renounceRole(bytes32 role, address account) external; +} diff --git a/lib/openzeppelin-contracts/contracts/access/IAccessControlEnumerable.sol b/lib/openzeppelin-contracts/contracts/access/IAccessControlEnumerable.sol new file mode 100644 index 0000000..61aaf57 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/IAccessControlEnumerable.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol) + +pragma solidity ^0.8.0; + +import "./IAccessControl.sol"; + +/** + * @dev External interface of AccessControlEnumerable declared to support ERC165 detection. + */ +interface IAccessControlEnumerable is IAccessControl { + /** + * @dev Returns one of the accounts that have `role`. `index` must be a + * value between 0 and {getRoleMemberCount}, non-inclusive. + * + * Role bearers are not sorted in any particular way, and their ordering may + * change at any point. + * + * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure + * you perform all queries on the same block. See the following + * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] + * for more information. + */ + function getRoleMember(bytes32 role, uint256 index) external view returns (address); + + /** + * @dev Returns the number of accounts that have `role`. Can be used + * together with {getRoleMember} to enumerate all bearers of a role. + */ + function getRoleMemberCount(bytes32 role) external view returns (uint256); +} diff --git a/lib/openzeppelin-contracts/contracts/access/Ownable.sol b/lib/openzeppelin-contracts/contracts/access/Ownable.sol new file mode 100644 index 0000000..6d4e866 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/Ownable.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() { + _transferOwnership(_msgSender()); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + require(owner() == _msgSender(), "Ownable: caller is not the owner"); + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), "Ownable: new owner is the zero address"); + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} diff --git a/lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol b/lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol new file mode 100644 index 0000000..2217063 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol) + +pragma solidity ^0.8.0; + +import "./Ownable.sol"; + +/** + * @dev Contract module which provides access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership} and {acceptOwnership}. + * + * This module is used through inheritance. It will make available all functions + * from parent (Ownable). + */ +abstract contract Ownable2Step is Ownable { + address private _pendingOwner; + + event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Returns the address of the pending owner. + */ + function pendingOwner() public view virtual returns (address) { + return _pendingOwner; + } + + /** + * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual override onlyOwner { + _pendingOwner = newOwner; + emit OwnershipTransferStarted(owner(), newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual override { + delete _pendingOwner; + super._transferOwnership(newOwner); + } + + /** + * @dev The new owner accepts the ownership transfer. + */ + function acceptOwnership() external { + address sender = _msgSender(); + require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner"); + _transferOwnership(sender); + } +} diff --git a/lib/openzeppelin-contracts/contracts/access/README.adoc b/lib/openzeppelin-contracts/contracts/access/README.adoc new file mode 100644 index 0000000..888d0e9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/access/README.adoc @@ -0,0 +1,25 @@ += Access Control + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/access + +This directory provides ways to restrict who can access the functions of a contract or when they can do it. + +- {AccessControl} provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. +- {Ownable} is a simpler mechanism with a single owner "role" that can be assigned to a single account. This simpler mechanism can be useful for quick tests but projects with production concerns are likely to outgrow it. + +== Authorization + +{{Ownable}} + +{{Ownable2Step}} + +{{IAccessControl}} + +{{AccessControl}} + +{{AccessControlCrossChain}} + +{{IAccessControlEnumerable}} + +{{AccessControlEnumerable}} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/CrossChainEnabled.sol b/lib/openzeppelin-contracts/contracts/crosschain/CrossChainEnabled.sol new file mode 100644 index 0000000..4c9b9e5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/CrossChainEnabled.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/CrossChainEnabled.sol) + +pragma solidity ^0.8.4; + +import "./errors.sol"; + +/** + * @dev Provides information for building cross-chain aware contracts. This + * abstract contract provides accessors and modifiers to control the execution + * flow when receiving cross-chain messages. + * + * Actual implementations of cross-chain aware contracts, which are based on + * this abstraction, will have to inherit from a bridge-specific + * specialization. Such specializations are provided under + * `crosschain//CrossChainEnabled.sol`. + * + * _Available since v4.6._ + */ +abstract contract CrossChainEnabled { + /** + * @dev Throws if the current function call is not the result of a + * cross-chain execution. + */ + modifier onlyCrossChain() { + if (!_isCrossChain()) revert NotCrossChainCall(); + _; + } + + /** + * @dev Throws if the current function call is not the result of a + * cross-chain execution initiated by `account`. + */ + modifier onlyCrossChainSender(address expected) { + address actual = _crossChainSender(); + if (expected != actual) revert InvalidCrossChainSender(actual, expected); + _; + } + + /** + * @dev Returns whether the current function call is the result of a + * cross-chain message. + */ + function _isCrossChain() internal view virtual returns (bool); + + /** + * @dev Returns the address of the sender of the cross-chain message that + * triggered the current function call. + * + * IMPORTANT: Should revert with `NotCrossChainCall` if the current function + * call is not the result of a cross-chain message. + */ + function _crossChainSender() internal view virtual returns (address); +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/README.adoc b/lib/openzeppelin-contracts/contracts/crosschain/README.adoc new file mode 100644 index 0000000..266b153 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/README.adoc @@ -0,0 +1,34 @@ += Cross Chain Awareness + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/crosschain + +This directory provides building blocks to improve cross-chain awareness of smart contracts. + +- {CrossChainEnabled} is an abstraction that contains accessors and modifiers to control the execution flow when receiving cross-chain messages. + +== CrossChainEnabled specializations + +The following specializations of {CrossChainEnabled} provide implementations of the {CrossChainEnabled} abstraction for specific bridges. This can be used to complex cross-chain aware components such as {AccessControlCrossChain}. + +{{CrossChainEnabledAMB}} + +{{CrossChainEnabledArbitrumL1}} + +{{CrossChainEnabledArbitrumL2}} + +{{CrossChainEnabledOptimism}} + +{{CrossChainEnabledPolygonChild}} + +== Libraries for cross-chain + +In addition to the {CrossChainEnabled} abstraction, cross-chain awareness is also available through libraries. These libraries can be used to build complex designs such as contracts with the ability to interact with multiple bridges. + +{{LibAMB}} + +{{LibArbitrumL1}} + +{{LibArbitrumL2}} + +{{LibOptimism}} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/amb/CrossChainEnabledAMB.sol b/lib/openzeppelin-contracts/contracts/crosschain/amb/CrossChainEnabledAMB.sol new file mode 100644 index 0000000..e69355d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/amb/CrossChainEnabledAMB.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/amb/CrossChainEnabledAMB.sol) + +pragma solidity ^0.8.4; + +import "../CrossChainEnabled.sol"; +import "./LibAMB.sol"; + +/** + * @dev https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB] + * specialization or the {CrossChainEnabled} abstraction. + * + * As of february 2020, AMB bridges are available between the following chains: + * + * - https://docs.tokenbridge.net/eth-xdai-amb-bridge/about-the-eth-xdai-amb[ETH ⇌ xDai] + * - https://docs.tokenbridge.net/eth-qdai-bridge/about-the-eth-qdai-amb[ETH ⇌ qDai] + * - https://docs.tokenbridge.net/eth-etc-amb-bridge/about-the-eth-etc-amb[ETH ⇌ ETC] + * - https://docs.tokenbridge.net/eth-bsc-amb/about-the-eth-bsc-amb[ETH ⇌ BSC] + * - https://docs.tokenbridge.net/eth-poa-amb-bridge/about-the-eth-poa-amb[ETH ⇌ POA] + * - https://docs.tokenbridge.net/bsc-xdai-amb/about-the-bsc-xdai-amb[BSC ⇌ xDai] + * - https://docs.tokenbridge.net/poa-xdai-amb/about-the-poa-xdai-amb[POA ⇌ xDai] + * - https://docs.tokenbridge.net/rinkeby-xdai-amb-bridge/about-the-rinkeby-xdai-amb[Rinkeby ⇌ xDai] + * - https://docs.tokenbridge.net/kovan-sokol-amb-bridge/about-the-kovan-sokol-amb[Kovan ⇌ Sokol] + * + * _Available since v4.6._ + */ +contract CrossChainEnabledAMB is CrossChainEnabled { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address private immutable _bridge; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address bridge) { + _bridge = bridge; + } + + /** + * @dev see {CrossChainEnabled-_isCrossChain} + */ + function _isCrossChain() internal view virtual override returns (bool) { + return LibAMB.isCrossChain(_bridge); + } + + /** + * @dev see {CrossChainEnabled-_crossChainSender} + */ + function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { + return LibAMB.crossChainSender(_bridge); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/amb/LibAMB.sol b/lib/openzeppelin-contracts/contracts/crosschain/amb/LibAMB.sol new file mode 100644 index 0000000..aef9c43 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/amb/LibAMB.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/amb/LibAMB.sol) + +pragma solidity ^0.8.4; + +import {IAMB as AMB_Bridge} from "../../vendor/amb/IAMB.sol"; +import "../errors.sol"; + +/** + * @dev Primitives for cross-chain aware contracts using the + * https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB] + * family of bridges. + */ +library LibAMB { + /** + * @dev Returns whether the current function call is the result of a + * cross-chain message relayed by `bridge`. + */ + function isCrossChain(address bridge) internal view returns (bool) { + return msg.sender == bridge; + } + + /** + * @dev Returns the address of the sender that triggered the current + * cross-chain message through `bridge`. + * + * NOTE: {isCrossChain} should be checked before trying to recover the + * sender, as it will revert with `NotCrossChainCall` if the current + * function call is not the result of a cross-chain message. + */ + function crossChainSender(address bridge) internal view returns (address) { + if (!isCrossChain(bridge)) revert NotCrossChainCall(); + return AMB_Bridge(bridge).messageSender(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol new file mode 100644 index 0000000..5068da3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol) + +pragma solidity ^0.8.4; + +import "../CrossChainEnabled.sol"; +import "./LibArbitrumL1.sol"; + +/** + * @dev https://arbitrum.io/[Arbitrum] specialization or the + * {CrossChainEnabled} abstraction the L1 side (mainnet). + * + * This version should only be deployed on L1 to process cross-chain messages + * originating from L2. For the other side, use {CrossChainEnabledArbitrumL2}. + * + * The bridge contract is provided and maintained by the arbitrum team. You can + * find the address of this contract on the rinkeby testnet in + * https://developer.offchainlabs.com/docs/useful_addresses[Arbitrum's developer documentation]. + * + * _Available since v4.6._ + */ +abstract contract CrossChainEnabledArbitrumL1 is CrossChainEnabled { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address private immutable _bridge; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address bridge) { + _bridge = bridge; + } + + /** + * @dev see {CrossChainEnabled-_isCrossChain} + */ + function _isCrossChain() internal view virtual override returns (bool) { + return LibArbitrumL1.isCrossChain(_bridge); + } + + /** + * @dev see {CrossChainEnabled-_crossChainSender} + */ + function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { + return LibArbitrumL1.crossChainSender(_bridge); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol new file mode 100644 index 0000000..e85993d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol) + +pragma solidity ^0.8.4; + +import "../CrossChainEnabled.sol"; +import "./LibArbitrumL2.sol"; + +/** + * @dev https://arbitrum.io/[Arbitrum] specialization or the + * {CrossChainEnabled} abstraction the L2 side (arbitrum). + * + * This version should only be deployed on L2 to process cross-chain messages + * originating from L1. For the other side, use {CrossChainEnabledArbitrumL1}. + * + * Arbitrum L2 includes the `ArbSys` contract at a fixed address. Therefore, + * this specialization of {CrossChainEnabled} does not include a constructor. + * + * _Available since v4.6._ + * + * WARNING: There is currently a bug in Arbitrum that causes this contract to + * fail to detect cross-chain calls when deployed behind a proxy. This will be + * fixed when the network is upgraded to Arbitrum Nitro, currently scheduled for + * August 31st 2022. + */ +abstract contract CrossChainEnabledArbitrumL2 is CrossChainEnabled { + /** + * @dev see {CrossChainEnabled-_isCrossChain} + */ + function _isCrossChain() internal view virtual override returns (bool) { + return LibArbitrumL2.isCrossChain(LibArbitrumL2.ARBSYS); + } + + /** + * @dev see {CrossChainEnabled-_crossChainSender} + */ + function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { + return LibArbitrumL2.crossChainSender(LibArbitrumL2.ARBSYS); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/LibArbitrumL1.sol b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/LibArbitrumL1.sol new file mode 100644 index 0000000..be7236b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/LibArbitrumL1.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (crosschain/arbitrum/LibArbitrumL1.sol) + +pragma solidity ^0.8.4; + +import {IBridge as ArbitrumL1_Bridge} from "../../vendor/arbitrum/IBridge.sol"; +import {IOutbox as ArbitrumL1_Outbox} from "../../vendor/arbitrum/IOutbox.sol"; +import "../errors.sol"; + +/** + * @dev Primitives for cross-chain aware contracts for + * https://arbitrum.io/[Arbitrum]. + * + * This version should only be used on L1 to process cross-chain messages + * originating from L2. For the other side, use {LibArbitrumL2}. + */ +library LibArbitrumL1 { + /** + * @dev Returns whether the current function call is the result of a + * cross-chain message relayed by the `bridge`. + */ + function isCrossChain(address bridge) internal view returns (bool) { + return msg.sender == bridge; + } + + /** + * @dev Returns the address of the sender that triggered the current + * cross-chain message through the `bridge`. + * + * NOTE: {isCrossChain} should be checked before trying to recover the + * sender, as it will revert with `NotCrossChainCall` if the current + * function call is not the result of a cross-chain message. + */ + function crossChainSender(address bridge) internal view returns (address) { + if (!isCrossChain(bridge)) revert NotCrossChainCall(); + + address sender = ArbitrumL1_Outbox(ArbitrumL1_Bridge(bridge).activeOutbox()).l2ToL1Sender(); + require(sender != address(0), "LibArbitrumL1: system messages without sender"); + + return sender; + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/LibArbitrumL2.sol b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/LibArbitrumL2.sol new file mode 100644 index 0000000..715a387 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/arbitrum/LibArbitrumL2.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (crosschain/arbitrum/LibArbitrumL2.sol) + +pragma solidity ^0.8.4; + +import {IArbSys as ArbitrumL2_Bridge} from "../../vendor/arbitrum/IArbSys.sol"; +import "../errors.sol"; + +/** + * @dev Primitives for cross-chain aware contracts for + * https://arbitrum.io/[Arbitrum]. + * + * This version should only be used on L2 to process cross-chain messages + * originating from L1. For the other side, use {LibArbitrumL1}. + * + * WARNING: There is currently a bug in Arbitrum that causes this contract to + * fail to detect cross-chain calls when deployed behind a proxy. This will be + * fixed when the network is upgraded to Arbitrum Nitro, currently scheduled for + * August 31st 2022. + */ +library LibArbitrumL2 { + /** + * @dev Returns whether the current function call is the result of a + * cross-chain message relayed by `arbsys`. + */ + address public constant ARBSYS = 0x0000000000000000000000000000000000000064; + + function isCrossChain(address arbsys) internal view returns (bool) { + return ArbitrumL2_Bridge(arbsys).wasMyCallersAddressAliased(); + } + + /** + * @dev Returns the address of the sender that triggered the current + * cross-chain message through `arbsys`. + * + * NOTE: {isCrossChain} should be checked before trying to recover the + * sender, as it will revert with `NotCrossChainCall` if the current + * function call is not the result of a cross-chain message. + */ + function crossChainSender(address arbsys) internal view returns (address) { + if (!isCrossChain(arbsys)) revert NotCrossChainCall(); + + return ArbitrumL2_Bridge(arbsys).myCallersAddressWithoutAliasing(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/errors.sol b/lib/openzeppelin-contracts/contracts/crosschain/errors.sol new file mode 100644 index 0000000..004460e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/errors.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/errors.sol) + +pragma solidity ^0.8.4; + +error NotCrossChainCall(); +error InvalidCrossChainSender(address actual, address expected); diff --git a/lib/openzeppelin-contracts/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol b/lib/openzeppelin-contracts/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol new file mode 100644 index 0000000..1005864 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/CrossChainEnabledOptimism.sol) + +pragma solidity ^0.8.4; + +import "../CrossChainEnabled.sol"; +import "./LibOptimism.sol"; + +/** + * @dev https://www.optimism.io/[Optimism] specialization or the + * {CrossChainEnabled} abstraction. + * + * The messenger (`CrossDomainMessenger`) contract is provided and maintained by + * the optimism team. You can find the address of this contract on mainnet and + * kovan in the https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts/deployments[deployments section of Optimism monorepo]. + * + * _Available since v4.6._ + */ +abstract contract CrossChainEnabledOptimism is CrossChainEnabled { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address private immutable _messenger; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address messenger) { + _messenger = messenger; + } + + /** + * @dev see {CrossChainEnabled-_isCrossChain} + */ + function _isCrossChain() internal view virtual override returns (bool) { + return LibOptimism.isCrossChain(_messenger); + } + + /** + * @dev see {CrossChainEnabled-_crossChainSender} + */ + function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { + return LibOptimism.crossChainSender(_messenger); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/optimism/LibOptimism.sol b/lib/openzeppelin-contracts/contracts/crosschain/optimism/LibOptimism.sol new file mode 100644 index 0000000..d963ade --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/optimism/LibOptimism.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/LibOptimism.sol) + +pragma solidity ^0.8.4; + +import {ICrossDomainMessenger as Optimism_Bridge} from "../../vendor/optimism/ICrossDomainMessenger.sol"; +import "../errors.sol"; + +/** + * @dev Primitives for cross-chain aware contracts for https://www.optimism.io/[Optimism]. + * See the https://community.optimism.io/docs/developers/bridge/messaging/#accessing-msg-sender[documentation] + * for the functionality used here. + */ +library LibOptimism { + /** + * @dev Returns whether the current function call is the result of a + * cross-chain message relayed by `messenger`. + */ + function isCrossChain(address messenger) internal view returns (bool) { + return msg.sender == messenger; + } + + /** + * @dev Returns the address of the sender that triggered the current + * cross-chain message through `messenger`. + * + * NOTE: {isCrossChain} should be checked before trying to recover the + * sender, as it will revert with `NotCrossChainCall` if the current + * function call is not the result of a cross-chain message. + */ + function crossChainSender(address messenger) internal view returns (address) { + if (!isCrossChain(messenger)) revert NotCrossChainCall(); + + return Optimism_Bridge(messenger).xDomainMessageSender(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol b/lib/openzeppelin-contracts/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol new file mode 100644 index 0000000..fa09948 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/crosschain/polygon/CrossChainEnabledPolygonChild.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/polygon/CrossChainEnabledPolygonChild.sol) + +pragma solidity ^0.8.4; + +import "../CrossChainEnabled.sol"; +import "../../security/ReentrancyGuard.sol"; +import "../../utils/Address.sol"; +import "../../vendor/polygon/IFxMessageProcessor.sol"; + +address constant DEFAULT_SENDER = 0x000000000000000000000000000000000000dEaD; + +/** + * @dev https://polygon.technology/[Polygon] specialization or the + * {CrossChainEnabled} abstraction the child side (polygon/mumbai). + * + * This version should only be deployed on child chain to process cross-chain + * messages originating from the parent chain. + * + * The fxChild contract is provided and maintained by the polygon team. You can + * find the address of this contract polygon and mumbai in + * https://docs.polygon.technology/docs/develop/l1-l2-communication/fx-portal/#contract-addresses[Polygon's Fx-Portal documentation]. + * + * _Available since v4.6._ + */ +abstract contract CrossChainEnabledPolygonChild is IFxMessageProcessor, CrossChainEnabled, ReentrancyGuard { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address private immutable _fxChild; + address private _sender = DEFAULT_SENDER; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address fxChild) { + _fxChild = fxChild; + } + + /** + * @dev see {CrossChainEnabled-_isCrossChain} + */ + function _isCrossChain() internal view virtual override returns (bool) { + return msg.sender == _fxChild; + } + + /** + * @dev see {CrossChainEnabled-_crossChainSender} + */ + function _crossChainSender() internal view virtual override onlyCrossChain returns (address) { + return _sender; + } + + /** + * @dev External entry point to receive and relay messages originating + * from the fxChild. + * + * Non-reentrancy is crucial to avoid a cross-chain call being able + * to impersonate anyone by just looping through this with user-defined + * arguments. + * + * Note: if _fxChild calls any other function that does a delegate-call, + * then security could be compromised. + */ + function processMessageFromRoot( + uint256 /* stateId */, + address rootMessageSender, + bytes calldata data + ) external override nonReentrant { + if (!_isCrossChain()) revert NotCrossChainCall(); + + _sender = rootMessageSender; + Address.functionDelegateCall(address(this), data, "cross-chain execution failed"); + _sender = DEFAULT_SENDER; + } +} diff --git a/lib/openzeppelin-contracts/contracts/finance/PaymentSplitter.sol b/lib/openzeppelin-contracts/contracts/finance/PaymentSplitter.sol new file mode 100644 index 0000000..daa9090 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/finance/PaymentSplitter.sol @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (finance/PaymentSplitter.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/utils/SafeERC20.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; + +/** + * @title PaymentSplitter + * @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware + * that the Ether will be split in this way, since it is handled transparently by the contract. + * + * The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each + * account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim + * an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the + * time of contract deployment and can't be updated thereafter. + * + * `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the + * accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release} + * function. + * + * NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and + * tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you + * to run tests before sending real value to this contract. + */ +contract PaymentSplitter is Context { + event PayeeAdded(address account, uint256 shares); + event PaymentReleased(address to, uint256 amount); + event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount); + event PaymentReceived(address from, uint256 amount); + + uint256 private _totalShares; + uint256 private _totalReleased; + + mapping(address => uint256) private _shares; + mapping(address => uint256) private _released; + address[] private _payees; + + mapping(IERC20 => uint256) private _erc20TotalReleased; + mapping(IERC20 => mapping(address => uint256)) private _erc20Released; + + /** + * @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at + * the matching position in the `shares` array. + * + * All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no + * duplicates in `payees`. + */ + constructor(address[] memory payees, uint256[] memory shares_) payable { + require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch"); + require(payees.length > 0, "PaymentSplitter: no payees"); + + for (uint256 i = 0; i < payees.length; i++) { + _addPayee(payees[i], shares_[i]); + } + } + + /** + * @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully + * reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the + * reliability of the events, and not the actual splitting of Ether. + * + * To learn more about this see the Solidity documentation for + * https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback + * functions]. + */ + receive() external payable virtual { + emit PaymentReceived(_msgSender(), msg.value); + } + + /** + * @dev Getter for the total shares held by payees. + */ + function totalShares() public view returns (uint256) { + return _totalShares; + } + + /** + * @dev Getter for the total amount of Ether already released. + */ + function totalReleased() public view returns (uint256) { + return _totalReleased; + } + + /** + * @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20 + * contract. + */ + function totalReleased(IERC20 token) public view returns (uint256) { + return _erc20TotalReleased[token]; + } + + /** + * @dev Getter for the amount of shares held by an account. + */ + function shares(address account) public view returns (uint256) { + return _shares[account]; + } + + /** + * @dev Getter for the amount of Ether already released to a payee. + */ + function released(address account) public view returns (uint256) { + return _released[account]; + } + + /** + * @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an + * IERC20 contract. + */ + function released(IERC20 token, address account) public view returns (uint256) { + return _erc20Released[token][account]; + } + + /** + * @dev Getter for the address of the payee number `index`. + */ + function payee(uint256 index) public view returns (address) { + return _payees[index]; + } + + /** + * @dev Getter for the amount of payee's releasable Ether. + */ + function releasable(address account) public view returns (uint256) { + uint256 totalReceived = address(this).balance + totalReleased(); + return _pendingPayment(account, totalReceived, released(account)); + } + + /** + * @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an + * IERC20 contract. + */ + function releasable(IERC20 token, address account) public view returns (uint256) { + uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token); + return _pendingPayment(account, totalReceived, released(token, account)); + } + + /** + * @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the + * total shares and their previous withdrawals. + */ + function release(address payable account) public virtual { + require(_shares[account] > 0, "PaymentSplitter: account has no shares"); + + uint256 payment = releasable(account); + + require(payment != 0, "PaymentSplitter: account is not due payment"); + + // _totalReleased is the sum of all values in _released. + // If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow. + _totalReleased += payment; + unchecked { + _released[account] += payment; + } + + Address.sendValue(account, payment); + emit PaymentReleased(account, payment); + } + + /** + * @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their + * percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20 + * contract. + */ + function release(IERC20 token, address account) public virtual { + require(_shares[account] > 0, "PaymentSplitter: account has no shares"); + + uint256 payment = releasable(token, account); + + require(payment != 0, "PaymentSplitter: account is not due payment"); + + // _erc20TotalReleased[token] is the sum of all values in _erc20Released[token]. + // If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment" + // cannot overflow. + _erc20TotalReleased[token] += payment; + unchecked { + _erc20Released[token][account] += payment; + } + + SafeERC20.safeTransfer(token, account, payment); + emit ERC20PaymentReleased(token, account, payment); + } + + /** + * @dev internal logic for computing the pending payment of an `account` given the token historical balances and + * already released amounts. + */ + function _pendingPayment( + address account, + uint256 totalReceived, + uint256 alreadyReleased + ) private view returns (uint256) { + return (totalReceived * _shares[account]) / _totalShares - alreadyReleased; + } + + /** + * @dev Add a new payee to the contract. + * @param account The address of the payee to add. + * @param shares_ The number of shares owned by the payee. + */ + function _addPayee(address account, uint256 shares_) private { + require(account != address(0), "PaymentSplitter: account is the zero address"); + require(shares_ > 0, "PaymentSplitter: shares are 0"); + require(_shares[account] == 0, "PaymentSplitter: account already has shares"); + + _payees.push(account); + _shares[account] = shares_; + _totalShares = _totalShares + shares_; + emit PayeeAdded(account, shares_); + } +} diff --git a/lib/openzeppelin-contracts/contracts/finance/README.adoc b/lib/openzeppelin-contracts/contracts/finance/README.adoc new file mode 100644 index 0000000..b64af31 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/finance/README.adoc @@ -0,0 +1,20 @@ += Finance + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance + +This directory includes primitives for financial systems: + +- {PaymentSplitter} allows to split Ether and ERC20 payments among a group of accounts. The sender does not need to be + aware that the assets will be split in this way, since it is handled transparently by the contract. The split can be + in equal parts or in any other arbitrary proportion. + +- {VestingWallet} handles the vesting of Ether and ERC20 tokens for a given beneficiary. Custody of multiple tokens can + be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting + schedule. + +== Contracts + +{{PaymentSplitter}} + +{{VestingWallet}} diff --git a/lib/openzeppelin-contracts/contracts/finance/VestingWallet.sol b/lib/openzeppelin-contracts/contracts/finance/VestingWallet.sol new file mode 100644 index 0000000..fe67eb5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/finance/VestingWallet.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (finance/VestingWallet.sol) +pragma solidity ^0.8.0; + +import "../token/ERC20/utils/SafeERC20.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; + +/** + * @title VestingWallet + * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens + * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule. + * The vesting schedule is customizable through the {vestedAmount} function. + * + * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning. + * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) + * be immediately releasable. + */ +contract VestingWallet is Context { + event EtherReleased(uint256 amount); + event ERC20Released(address indexed token, uint256 amount); + + uint256 private _released; + mapping(address => uint256) private _erc20Released; + address private immutable _beneficiary; + uint64 private immutable _start; + uint64 private immutable _duration; + + /** + * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet. + */ + constructor(address beneficiaryAddress, uint64 startTimestamp, uint64 durationSeconds) payable { + require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address"); + _beneficiary = beneficiaryAddress; + _start = startTimestamp; + _duration = durationSeconds; + } + + /** + * @dev The contract should be able to receive Eth. + */ + receive() external payable virtual {} + + /** + * @dev Getter for the beneficiary address. + */ + function beneficiary() public view virtual returns (address) { + return _beneficiary; + } + + /** + * @dev Getter for the start timestamp. + */ + function start() public view virtual returns (uint256) { + return _start; + } + + /** + * @dev Getter for the vesting duration. + */ + function duration() public view virtual returns (uint256) { + return _duration; + } + + /** + * @dev Amount of eth already released + */ + function released() public view virtual returns (uint256) { + return _released; + } + + /** + * @dev Amount of token already released + */ + function released(address token) public view virtual returns (uint256) { + return _erc20Released[token]; + } + + /** + * @dev Getter for the amount of releasable eth. + */ + function releasable() public view virtual returns (uint256) { + return vestedAmount(uint64(block.timestamp)) - released(); + } + + /** + * @dev Getter for the amount of releasable `token` tokens. `token` should be the address of an + * IERC20 contract. + */ + function releasable(address token) public view virtual returns (uint256) { + return vestedAmount(token, uint64(block.timestamp)) - released(token); + } + + /** + * @dev Release the native token (ether) that have already vested. + * + * Emits a {EtherReleased} event. + */ + function release() public virtual { + uint256 amount = releasable(); + _released += amount; + emit EtherReleased(amount); + Address.sendValue(payable(beneficiary()), amount); + } + + /** + * @dev Release the tokens that have already vested. + * + * Emits a {ERC20Released} event. + */ + function release(address token) public virtual { + uint256 amount = releasable(token); + _erc20Released[token] += amount; + emit ERC20Released(token, amount); + SafeERC20.safeTransfer(IERC20(token), beneficiary(), amount); + } + + /** + * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve. + */ + function vestedAmount(uint64 timestamp) public view virtual returns (uint256) { + return _vestingSchedule(address(this).balance + released(), timestamp); + } + + /** + * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve. + */ + function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) { + return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp); + } + + /** + * @dev Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for + * an asset given its total historical allocation. + */ + function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) { + if (timestamp < start()) { + return 0; + } else if (timestamp > start() + duration()) { + return totalAllocation; + } else { + return (totalAllocation * (timestamp - start())) / duration(); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/Governor.sol b/lib/openzeppelin-contracts/contracts/governance/Governor.sol new file mode 100644 index 0000000..8235fb5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/Governor.sol @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/Governor.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; +import "../token/ERC1155/IERC1155Receiver.sol"; +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/EIP712.sol"; +import "../utils/introspection/ERC165.sol"; +import "../utils/math/SafeCast.sol"; +import "../utils/structs/DoubleEndedQueue.sol"; +import "../utils/Address.sol"; +import "../utils/Context.sol"; +import "../utils/Timers.sol"; +import "./IGovernor.sol"; + +/** + * @dev Core of the governance system, designed to be extended though various modules. + * + * This contract is abstract and requires several function to be implemented in various modules: + * + * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote} + * - A voting module must implement {_getVotes} + * - Additionally, the {votingPeriod} must also be implemented + * + * _Available since v4.3._ + */ +abstract contract Governor is Context, ERC165, EIP712, IGovernor, IERC721Receiver, IERC1155Receiver { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + + bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)"); + bytes32 public constant EXTENDED_BALLOT_TYPEHASH = + keccak256("ExtendedBallot(uint256 proposalId,uint8 support,string reason,bytes params)"); + + struct ProposalCore { + Timers.BlockNumber voteStart; + Timers.BlockNumber voteEnd; + bool executed; + bool canceled; + } + + string private _name; + + mapping(uint256 => ProposalCore) private _proposals; + + // This queue keeps track of the governor operating on itself. Calls to functions protected by the + // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, + // consumed by the {onlyGovernance} modifier and eventually reset in {_afterExecute}. This ensures that the + // execution of {onlyGovernance} protected calls can only be achieved through successful proposals. + DoubleEndedQueue.Bytes32Deque private _governanceCall; + + /** + * @dev Restricts a function so it can only be executed through governance proposals. For example, governance + * parameter setters in {GovernorSettings} are protected using this modifier. + * + * The governance executing address may be different from the Governor's own address, for example it could be a + * timelock. This can be customized by modules by overriding {_executor}. The executor is only able to invoke these + * functions during the execution of the governor's {execute} function, and not under any other circumstances. Thus, + * for example, additional timelock proposers are not able to change governance parameters without going through the + * governance protocol (since v4.6). + */ + modifier onlyGovernance() { + require(_msgSender() == _executor(), "Governor: onlyGovernance"); + if (_executor() != address(this)) { + bytes32 msgDataHash = keccak256(_msgData()); + // loop until popping the expected operation - throw if deque is empty (operation not authorized) + while (_governanceCall.popFront() != msgDataHash) {} + } + _; + } + + /** + * @dev Sets the value for {name} and {version} + */ + constructor(string memory name_) EIP712(name_, version()) { + _name = name_; + } + + /** + * @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract) + */ + receive() external payable virtual { + require(_executor() == address(this)); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + // In addition to the current interfaceId, also support previous version of the interfaceId that did not + // include the castVoteWithReasonAndParams() function as standard + return + interfaceId == + (type(IGovernor).interfaceId ^ + this.castVoteWithReasonAndParams.selector ^ + this.castVoteWithReasonAndParamsBySig.selector ^ + this.getVotesWithParams.selector) || + interfaceId == type(IGovernor).interfaceId || + interfaceId == type(IERC1155Receiver).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IGovernor-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IGovernor-version}. + */ + function version() public view virtual override returns (string memory) { + return "1"; + } + + /** + * @dev See {IGovernor-hashProposal}. + * + * The proposal id is produced by hashing the ABI encoded `targets` array, the `values` array, the `calldatas` array + * and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id + * can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in + * advance, before the proposal is submitted. + * + * Note that the chainId and the governor address are not part of the proposal id computation. Consequently, the + * same proposal (with same operation and same description) will have the same id if submitted on multiple governors + * across multiple networks. This also means that in order to execute the same operation twice (on the same + * governor) the proposer will have to change the description in order to avoid proposal id conflicts. + */ + function hashProposal( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public pure virtual override returns (uint256) { + return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash))); + } + + /** + * @dev See {IGovernor-state}. + */ + function state(uint256 proposalId) public view virtual override returns (ProposalState) { + ProposalCore storage proposal = _proposals[proposalId]; + + if (proposal.executed) { + return ProposalState.Executed; + } + + if (proposal.canceled) { + return ProposalState.Canceled; + } + + uint256 snapshot = proposalSnapshot(proposalId); + + if (snapshot == 0) { + revert("Governor: unknown proposal id"); + } + + if (snapshot >= block.number) { + return ProposalState.Pending; + } + + uint256 deadline = proposalDeadline(proposalId); + + if (deadline >= block.number) { + return ProposalState.Active; + } + + if (_quorumReached(proposalId) && _voteSucceeded(proposalId)) { + return ProposalState.Succeeded; + } else { + return ProposalState.Defeated; + } + } + + /** + * @dev See {IGovernor-proposalSnapshot}. + */ + function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) { + return _proposals[proposalId].voteStart.getDeadline(); + } + + /** + * @dev See {IGovernor-proposalDeadline}. + */ + function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { + return _proposals[proposalId].voteEnd.getDeadline(); + } + + /** + * @dev Part of the Governor Bravo's interface: _"The number of votes required in order for a voter to become a proposer"_. + */ + function proposalThreshold() public view virtual returns (uint256) { + return 0; + } + + /** + * @dev Amount of votes already cast passes the threshold limit. + */ + function _quorumReached(uint256 proposalId) internal view virtual returns (bool); + + /** + * @dev Is the proposal successful or not. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual returns (bool); + + /** + * @dev Get the voting weight of `account` at a specific `blockNumber`, for a vote as described by `params`. + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory params + ) internal view virtual returns (uint256); + + /** + * @dev Register a vote for `proposalId` by `account` with a given `support`, voting `weight` and voting `params`. + * + * Note: Support is generic and can represent various things depending on the voting system used. + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory params + ) internal virtual; + + /** + * @dev Default additional encoded parameters used by castVote methods that don't include them + * + * Note: Should be overridden by specific implementations to use an appropriate value, the + * meaning of the additional params, in the context of that implementation + */ + function _defaultParams() internal view virtual returns (bytes memory) { + return ""; + } + + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + require( + getVotes(_msgSender(), block.number - 1) >= proposalThreshold(), + "Governor: proposer votes below proposal threshold" + ); + + uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description))); + + require(targets.length == values.length, "Governor: invalid proposal length"); + require(targets.length == calldatas.length, "Governor: invalid proposal length"); + require(targets.length > 0, "Governor: empty proposal"); + + ProposalCore storage proposal = _proposals[proposalId]; + require(proposal.voteStart.isUnset(), "Governor: proposal already exists"); + + uint64 snapshot = block.number.toUint64() + votingDelay().toUint64(); + uint64 deadline = snapshot + votingPeriod().toUint64(); + + proposal.voteStart.setDeadline(snapshot); + proposal.voteEnd.setDeadline(deadline); + + emit ProposalCreated( + proposalId, + _msgSender(), + targets, + values, + new string[](targets.length), + calldatas, + snapshot, + deadline, + description + ); + + return proposalId; + } + + /** + * @dev See {IGovernor-execute}. + */ + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public payable virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + ProposalState status = state(proposalId); + require( + status == ProposalState.Succeeded || status == ProposalState.Queued, + "Governor: proposal not successful" + ); + _proposals[proposalId].executed = true; + + emit ProposalExecuted(proposalId); + + _beforeExecute(proposalId, targets, values, calldatas, descriptionHash); + _execute(proposalId, targets, values, calldatas, descriptionHash); + _afterExecute(proposalId, targets, values, calldatas, descriptionHash); + + return proposalId; + } + + /** + * @dev Internal execution mechanism. Can be overridden to implement different execution mechanism + */ + function _execute( + uint256 /* proposalId */, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual { + string memory errorMessage = "Governor: call reverted without message"; + for (uint256 i = 0; i < targets.length; ++i) { + (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]); + Address.verifyCallResult(success, returndata, errorMessage); + } + } + + /** + * @dev Hook before execution is triggered. + */ + function _beforeExecute( + uint256 /* proposalId */, + address[] memory targets, + uint256[] memory /* values */, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual { + if (_executor() != address(this)) { + for (uint256 i = 0; i < targets.length; ++i) { + if (targets[i] == address(this)) { + _governanceCall.pushBack(keccak256(calldatas[i])); + } + } + } + } + + /** + * @dev Hook after execution is triggered. + */ + function _afterExecute( + uint256 /* proposalId */, + address[] memory /* targets */, + uint256[] memory /* values */, + bytes[] memory /* calldatas */, + bytes32 /*descriptionHash*/ + ) internal virtual { + if (_executor() != address(this)) { + if (!_governanceCall.empty()) { + _governanceCall.clear(); + } + } + } + + /** + * @dev Internal cancel mechanism: locks up the proposal timer, preventing it from being re-submitted. Marks it as + * canceled to allow distinguishing it from executed proposals. + * + * Emits a {IGovernor-ProposalCanceled} event. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + ProposalState status = state(proposalId); + + require( + status != ProposalState.Canceled && status != ProposalState.Expired && status != ProposalState.Executed, + "Governor: proposal not active" + ); + _proposals[proposalId].canceled = true; + + emit ProposalCanceled(proposalId); + + return proposalId; + } + + /** + * @dev See {IGovernor-getVotes}. + */ + function getVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + return _getVotes(account, blockNumber, _defaultParams()); + } + + /** + * @dev See {IGovernor-getVotesWithParams}. + */ + function getVotesWithParams( + address account, + uint256 blockNumber, + bytes memory params + ) public view virtual override returns (uint256) { + return _getVotes(account, blockNumber, params); + } + + /** + * @dev See {IGovernor-castVote}. + */ + function castVote(uint256 proposalId, uint8 support) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, ""); + } + + /** + * @dev See {IGovernor-castVoteWithReason}. + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, reason); + } + + /** + * @dev See {IGovernor-castVoteWithReasonAndParams}. + */ + function castVoteWithReasonAndParams( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params + ) public virtual override returns (uint256) { + address voter = _msgSender(); + return _castVote(proposalId, voter, support, reason, params); + } + + /** + * @dev See {IGovernor-castVoteBySig}. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support))), + v, + r, + s + ); + return _castVote(proposalId, voter, support, ""); + } + + /** + * @dev See {IGovernor-castVoteWithReasonAndParamsBySig}. + */ + function castVoteWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override returns (uint256) { + address voter = ECDSA.recover( + _hashTypedDataV4( + keccak256( + abi.encode( + EXTENDED_BALLOT_TYPEHASH, + proposalId, + support, + keccak256(bytes(reason)), + keccak256(params) + ) + ) + ), + v, + r, + s + ); + + return _castVote(proposalId, voter, support, reason, params); + } + + /** + * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve + * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. Uses the _defaultParams(). + * + * Emits a {IGovernor-VoteCast} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason + ) internal virtual returns (uint256) { + return _castVote(proposalId, account, support, reason, _defaultParams()); + } + + /** + * @dev Internal vote casting mechanism: Check that the vote is pending, that it has not been cast yet, retrieve + * voting weight using {IGovernor-getVotes} and call the {_countVote} internal function. + * + * Emits a {IGovernor-VoteCast} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal virtual returns (uint256) { + ProposalCore storage proposal = _proposals[proposalId]; + require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active"); + + uint256 weight = _getVotes(account, proposal.voteStart.getDeadline(), params); + _countVote(proposalId, account, support, weight, params); + + if (params.length == 0) { + emit VoteCast(account, proposalId, support, weight, reason); + } else { + emit VoteCastWithParams(account, proposalId, support, weight, reason, params); + } + + return weight; + } + + /** + * @dev Relays a transaction or function call to an arbitrary target. In cases where the governance executor + * is some contract other than the governor itself, like when using a timelock, this function can be invoked + * in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake. + * Note that if the executor is simply the governor itself, use of `relay` is redundant. + */ + function relay(address target, uint256 value, bytes calldata data) external payable virtual onlyGovernance { + (bool success, bytes memory returndata) = target.call{value: value}(data); + Address.verifyCallResult(success, returndata, "Governor: relay reverted without message"); + } + + /** + * @dev Address through which the governor executes action. Will be overloaded by module that execute actions + * through another contract such as a timelock. + */ + function _executor() internal view virtual returns (address) { + return address(this); + } + + /** + * @dev See {IERC721Receiver-onERC721Received}. + */ + function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } + + /** + * @dev See {IERC1155Receiver-onERC1155Received}. + */ + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + /** + * @dev See {IERC1155Receiver-onERC1155BatchReceived}. + */ + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/IGovernor.sol b/lib/openzeppelin-contracts/contracts/governance/IGovernor.sol new file mode 100644 index 0000000..eb3d1fc --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/IGovernor.sol @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/IGovernor.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165.sol"; + +/** + * @dev Interface of the {Governor} core. + * + * _Available since v4.3._ + */ +abstract contract IGovernor is IERC165 { + enum ProposalState { + Pending, + Active, + Canceled, + Defeated, + Succeeded, + Queued, + Expired, + Executed + } + + /** + * @dev Emitted when a proposal is created. + */ + event ProposalCreated( + uint256 proposalId, + address proposer, + address[] targets, + uint256[] values, + string[] signatures, + bytes[] calldatas, + uint256 startBlock, + uint256 endBlock, + string description + ); + + /** + * @dev Emitted when a proposal is canceled. + */ + event ProposalCanceled(uint256 proposalId); + + /** + * @dev Emitted when a proposal is executed. + */ + event ProposalExecuted(uint256 proposalId); + + /** + * @dev Emitted when a vote is cast without params. + * + * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used. + */ + event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason); + + /** + * @dev Emitted when a vote is cast with params. + * + * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used. + * `params` are additional encoded parameters. Their intepepretation also depends on the voting module used. + */ + event VoteCastWithParams( + address indexed voter, + uint256 proposalId, + uint8 support, + uint256 weight, + string reason, + bytes params + ); + + /** + * @notice module:core + * @dev Name of the governor instance (used in building the ERC712 domain separator). + */ + function name() public view virtual returns (string memory); + + /** + * @notice module:core + * @dev Version of the governor instance (used in building the ERC712 domain separator). Default: "1" + */ + function version() public view virtual returns (string memory); + + /** + * @notice module:voting + * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to + * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of + * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + * + * There are 2 standard keys: `support` and `quorum`. + * + * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. + * - `quorum=bravo` means that only For votes are counted towards quorum. + * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + * + * If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique + * name that describes the behavior. For example: + * + * - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. + * - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. + * + * NOTE: The string can be decoded by the standard + * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`] + * JavaScript class. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual returns (string memory); + + /** + * @notice module:core + * @dev Hashing function used to (re)build the proposal id from the proposal details.. + */ + function hashProposal( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public pure virtual returns (uint256); + + /** + * @notice module:core + * @dev Current state of a proposal, following Compound's convention + */ + function state(uint256 proposalId) public view virtual returns (ProposalState); + + /** + * @notice module:core + * @dev Block number used to retrieve user's votes and quorum. As per Compound's Comp and OpenZeppelin's + * ERC20Votes, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the + * beginning of the following block. + */ + function proposalSnapshot(uint256 proposalId) public view virtual returns (uint256); + + /** + * @notice module:core + * @dev Block number at which votes close. Votes close at the end of this block, so it is possible to cast a vote + * during this block. + */ + function proposalDeadline(uint256 proposalId) public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Delay, in number of block, between the proposal is created and the vote starts. This can be increassed to + * leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + */ + function votingDelay() public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Delay, in number of blocks, between the vote start and vote ends. + * + * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting + * duration compared to the voting delay. + */ + function votingPeriod() public view virtual returns (uint256); + + /** + * @notice module:user-config + * @dev Minimum number of cast voted required for a proposal to be successful. + * + * Note: The `blockNumber` parameter corresponds to the snapshot used for counting vote. This allows to scale the + * quorum depending on values such as the totalSupply of a token at this block (see {ERC20Votes}). + */ + function quorum(uint256 blockNumber) public view virtual returns (uint256); + + /** + * @notice module:reputation + * @dev Voting power of an `account` at a specific `blockNumber`. + * + * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or + * multiple), {ERC20Votes} tokens. + */ + function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256); + + /** + * @notice module:reputation + * @dev Voting power of an `account` at a specific `blockNumber` given additional encoded parameters. + */ + function getVotesWithParams( + address account, + uint256 blockNumber, + bytes memory params + ) public view virtual returns (uint256); + + /** + * @notice module:voting + * @dev Returns whether `account` has cast a vote on `proposalId`. + */ + function hasVoted(uint256 proposalId, address account) public view virtual returns (bool); + + /** + * @dev Create a new proposal. Vote start {IGovernor-votingDelay} blocks after the proposal is created and ends + * {IGovernor-votingPeriod} blocks after the voting starts. + * + * Emits a {ProposalCreated} event. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual returns (uint256 proposalId); + + /** + * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the + * deadline to be reached. + * + * Emits a {ProposalExecuted} event. + * + * Note: some module can modify the requirements for execution, for example by adding an additional timelock. + */ + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public payable virtual returns (uint256 proposalId); + + /** + * @dev Cast a vote + * + * Emits a {VoteCast} event. + */ + function castVote(uint256 proposalId, uint8 support) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote with a reason + * + * Emits a {VoteCast} event. + */ + function castVoteWithReason( + uint256 proposalId, + uint8 support, + string calldata reason + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote with a reason and additional encoded parameters + * + * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params. + */ + function castVoteWithReasonAndParams( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote using the user's cryptographic signature. + * + * Emits a {VoteCast} event. + */ + function castVoteBySig( + uint256 proposalId, + uint8 support, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (uint256 balance); + + /** + * @dev Cast a vote with a reason and additional encoded parameters using the user's cryptographic signature. + * + * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params. + */ + function castVoteWithReasonAndParamsBySig( + uint256 proposalId, + uint8 support, + string calldata reason, + bytes memory params, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (uint256 balance); +} diff --git a/lib/openzeppelin-contracts/contracts/governance/README.adoc b/lib/openzeppelin-contracts/contracts/governance/README.adoc new file mode 100644 index 0000000..f711c63 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/README.adoc @@ -0,0 +1,176 @@ += Governance + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/governance + +This directory includes primitives for on-chain governance. + +== Governor + +This modular system of Governor contracts allows the deployment on-chain voting protocols similar to https://compound.finance/docs/governance[Compound's Governor Alpha & Bravo] and beyond, through the ability to easily customize multiple aspects of the protocol. + +[TIP] +==== +For a guided experience, set up your Governor contract using https://wizard.openzeppelin.com/#governor[Contracts Wizard]. + +For a written walkthrough, check out our guide on xref:ROOT:governance.adoc[How to set up on-chain governance]. +==== + +* {Governor}: The core contract that contains all the logic and primitives. It is abstract and requires choosing one of each of the modules below, or custom ones. + +Votes modules determine the source of voting power, and sometimes quorum number. + +* {GovernorVotes}: Extracts voting weight from an {ERC20Votes} token. + +* {GovernorVotesComp}: Extracts voting weight from a COMP-like or {ERC20VotesComp} token. + +* {GovernorVotesQuorumFraction}: Combines with `GovernorVotes` to set the quorum as a fraction of the total token supply. + +Counting modules determine valid voting options. + +* {GovernorCountingSimple}: Simple voting mechanism with 3 voting options: Against, For and Abstain. + +Timelock extensions add a delay for governance decisions to be executed. The workflow is extended to require a `queue` step before execution. With these modules, proposals are executed by the external timelock contract, thus it is the timelock that has to hold the assets that are being governed. + +* {GovernorTimelockControl}: Connects with an instance of {TimelockController}. Allows multiple proposers and executors, in addition to the Governor itself. + +* {GovernorTimelockCompound}: Connects with an instance of Compound's https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[`Timelock`] contract. + +Other extensions can customize the behavior or interface in multiple ways. + +* {GovernorCompatibilityBravo}: Extends the interface to be fully `GovernorBravo`-compatible. Note that events are compatible regardless of whether this extension is included or not. + +* {GovernorSettings}: Manages some of the settings (voting delay, voting period duration, and proposal threshold) in a way that can be updated through a governance proposal, without requiring an upgrade. + +* {GovernorPreventLateQuorum}: Ensures there is a minimum voting period after quorum is reached as a security protection against large voters. + +In addition to modules and extensions, the core contract requires a few virtual functions to be implemented to your particular specifications: + +* <>: Delay (in number of blocks) since the proposal is submitted until voting power is fixed and voting starts. This can be used to enforce a delay after a proposal is published for users to buy tokens, or delegate their votes. +* <>: Delay (in number of blocks) since the proposal starts until voting ends. +* <>: Quorum required for a proposal to be successful. This function includes a `blockNumber` argument so the quorum can adapt through time, for example, to follow a token's `totalSupply`. + +NOTE: Functions of the `Governor` contract do not include access control. If you want to restrict access, you should add these checks by overloading the particular functions. Among these, {Governor-_cancel} is internal by default, and you will have to expose it (with the right access control mechanism) yourself if this function is needed. + +=== Core + +{{IGovernor}} + +{{Governor}} + +=== Modules + +{{GovernorCountingSimple}} + +{{GovernorVotes}} + +{{GovernorVotesQuorumFraction}} + +{{GovernorVotesComp}} + +=== Extensions + +{{GovernorTimelockControl}} + +{{GovernorTimelockCompound}} + +{{GovernorSettings}} + +{{GovernorPreventLateQuorum}} + +{{GovernorCompatibilityBravo}} + +=== Deprecated + +{{GovernorProposalThreshold}} + +== Utils + +{{Votes}} + +== Timelock + +In a governance system, the {TimelockController} contract is in charge of introducing a delay between a proposal and its execution. It can be used with or without a {Governor}. + +{{TimelockController}} + +[[timelock-terminology]] +==== Terminology + +* *Operation:* A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see xref:access-control.adoc#operation_lifecycle[operation lifecycle]). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content. +* *Operation status:* +** *Unset:* An operation that is not part of the timelock mechanism. +** *Pending:* An operation that has been scheduled, before the timer expires. +** *Ready:* An operation that has been scheduled, after the timer expires. +** *Done:* An operation that has been executed. +* *Predecessor*: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations. +* *Role*: +** *Admin:* An address (smart contract or EOA) that is in charge of granting the roles of Proposer and Executor. +** *Proposer:* An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations. +** *Executor:* An address (smart contract or EOA) that is in charge of executing operations once the timelock has expired. This role can be given to the zero address to allow anyone to execute operations. + +[[timelock-operation]] +==== Operation structure + +Operation executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations. + +Both operations contain: + +* *Target*, the address of the smart contract that the timelock should operate on. +* *Value*, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction. +* *Data*, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role `ROLE` to `ACCOUNT` can be encoded using web3js as follows: + +```javascript +const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI() +``` + +* *Predecessor*, that specifies a dependency between operations. This dependency is optional. Use `bytes32(0)` if the operation does not have any dependency. +* *Salt*, used to disambiguate two otherwise identical operations. This can be any random value. + +In the case of batched operations, `target`, `value` and `data` are specified as arrays, which must be of the same length. + +[[timelock-operation-lifecycle]] +==== Operation lifecycle + +Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle: + +`Unset` -> `Pending` -> `Pending` + `Ready` -> `Done` + +* By calling xref:api:governance.adoc#TimelockController-schedule-address-uint256-bytes-bytes32-bytes32-uint256-[`schedule`] (or xref:api:governance.adoc#TimelockController-scheduleBatch-address---uint256---bytes---bytes32-bytes32-uint256-[`scheduleBatch`]), a proposer moves the operation from the `Unset` to the `Pending` state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the xref:api:governance.adoc#TimelockController-getTimestamp-bytes32-[`getTimestamp`] method. +* Once the timer expires, the operation automatically gets the `Ready` state. At this point, it can be executed. +* By calling xref:api:governance.adoc#TimelockController-TimelockController-execute-address-uint256-bytes-bytes32-bytes32-[`execute`] (or xref:api:governance.adoc#TimelockController-executeBatch-address---uint256---bytes---bytes32-bytes32-[`executeBatch`]), an executor triggers the operation's underlying transactions and moves it to the `Done` state. If the operation has a predecessor, it has to be in the `Done` state for this transition to succeed. +* xref:api:governance.adoc#TimelockController-TimelockController-cancel-bytes32-[`cancel`] allows proposers to cancel any `Pending` operation. This resets the operation to the `Unset` state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled. + +Operations status can be queried using the functions: + +* xref:api:governance.adoc#TimelockController-isOperationPending-bytes32-[`isOperationPending(bytes32)`] +* xref:api:governance.adoc#TimelockController-isOperationReady-bytes32-[`isOperationReady(bytes32)`] +* xref:api:governance.adoc#TimelockController-isOperationDone-bytes32-[`isOperationDone(bytes32)`] + +[[timelock-roles]] +==== Roles + +[[timelock-admin]] +===== Admin + +The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, the admin role can be granted to any address (in addition to the timelock itself). After further configuration and testing, this optional admin should renounce its role such that all further maintenance operations have to go through the timelock process. + +This role is identified by the *TIMELOCK_ADMIN_ROLE* value: `0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5` + +[[timelock-proposer]] +===== Proposer + +The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO. + +WARNING: *Proposer fight:* Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers. + +This role is identified by the *PROPOSER_ROLE* value: `0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1` + +[[timelock-executor]] +===== Executor + +The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executors can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers. Alternatively, it is possible to allow _any_ address to execute a proposal once the timelock has expired by granting the executor role to the zero address. + +This role is identified by the *EXECUTOR_ROLE* value: `0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63` + +WARNING: A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the {AccessControl} documentation to learn more about role management. diff --git a/lib/openzeppelin-contracts/contracts/governance/TimelockController.sol b/lib/openzeppelin-contracts/contracts/governance/TimelockController.sol new file mode 100644 index 0000000..da71434 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/TimelockController.sol @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/TimelockController.sol) + +pragma solidity ^0.8.0; + +import "../access/AccessControl.sol"; +import "../token/ERC721/IERC721Receiver.sol"; +import "../token/ERC1155/IERC1155Receiver.sol"; +import "../utils/Address.sol"; + +/** + * @dev Contract module which acts as a timelocked controller. When set as the + * owner of an `Ownable` smart contract, it enforces a timelock on all + * `onlyOwner` maintenance operations. This gives time for users of the + * controlled contract to exit before a potentially dangerous maintenance + * operation is applied. + * + * By default, this contract is self administered, meaning administration tasks + * have to go through the timelock process. The proposer (resp executor) role + * is in charge of proposing (resp executing) operations. A common use case is + * to position this {TimelockController} as the owner of a smart contract, with + * a multisig or a DAO as the sole proposer. + * + * _Available since v3.3._ + */ +contract TimelockController is AccessControl, IERC721Receiver, IERC1155Receiver { + bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE"); + bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); + bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); + bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE"); + uint256 internal constant _DONE_TIMESTAMP = uint256(1); + + mapping(bytes32 => uint256) private _timestamps; + uint256 private _minDelay; + + /** + * @dev Emitted when a call is scheduled as part of operation `id`. + */ + event CallScheduled( + bytes32 indexed id, + uint256 indexed index, + address target, + uint256 value, + bytes data, + bytes32 predecessor, + uint256 delay + ); + + /** + * @dev Emitted when a call is performed as part of operation `id`. + */ + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); + + /** + * @dev Emitted when operation `id` is cancelled. + */ + event Cancelled(bytes32 indexed id); + + /** + * @dev Emitted when the minimum delay for future operations is modified. + */ + event MinDelayChange(uint256 oldDuration, uint256 newDuration); + + /** + * @dev Initializes the contract with the following parameters: + * + * - `minDelay`: initial minimum delay for operations + * - `proposers`: accounts to be granted proposer and canceller roles + * - `executors`: accounts to be granted executor role + * - `admin`: optional account to be granted admin role; disable with zero address + * + * IMPORTANT: The optional admin can aid with initial configuration of roles after deployment + * without being subject to delay, but this role should be subsequently renounced in favor of + * administration through timelocked proposals. Previous versions of this contract would assign + * this admin to the deployer automatically and should be renounced as well. + */ + constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) { + _setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE); + _setRoleAdmin(CANCELLER_ROLE, TIMELOCK_ADMIN_ROLE); + + // self administration + _setupRole(TIMELOCK_ADMIN_ROLE, address(this)); + + // optional admin + if (admin != address(0)) { + _setupRole(TIMELOCK_ADMIN_ROLE, admin); + } + + // register proposers and cancellers + for (uint256 i = 0; i < proposers.length; ++i) { + _setupRole(PROPOSER_ROLE, proposers[i]); + _setupRole(CANCELLER_ROLE, proposers[i]); + } + + // register executors + for (uint256 i = 0; i < executors.length; ++i) { + _setupRole(EXECUTOR_ROLE, executors[i]); + } + + _minDelay = minDelay; + emit MinDelayChange(0, minDelay); + } + + /** + * @dev Modifier to make a function callable only by a certain role. In + * addition to checking the sender's role, `address(0)` 's role is also + * considered. Granting a role to `address(0)` is equivalent to enabling + * this role for everyone. + */ + modifier onlyRoleOrOpenRole(bytes32 role) { + if (!hasRole(role, address(0))) { + _checkRole(role, _msgSender()); + } + _; + } + + /** + * @dev Contract might receive/hold ETH as part of the maintenance process. + */ + receive() external payable {} + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, AccessControl) returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Returns whether an id correspond to a registered operation. This + * includes both Pending, Ready and Done operations. + */ + function isOperation(bytes32 id) public view virtual returns (bool registered) { + return getTimestamp(id) > 0; + } + + /** + * @dev Returns whether an operation is pending or not. + */ + function isOperationPending(bytes32 id) public view virtual returns (bool pending) { + return getTimestamp(id) > _DONE_TIMESTAMP; + } + + /** + * @dev Returns whether an operation is ready or not. + */ + function isOperationReady(bytes32 id) public view virtual returns (bool ready) { + uint256 timestamp = getTimestamp(id); + return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp; + } + + /** + * @dev Returns whether an operation is done or not. + */ + function isOperationDone(bytes32 id) public view virtual returns (bool done) { + return getTimestamp(id) == _DONE_TIMESTAMP; + } + + /** + * @dev Returns the timestamp at which an operation becomes ready (0 for + * unset operations, 1 for done operations). + */ + function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) { + return _timestamps[id]; + } + + /** + * @dev Returns the minimum delay for an operation to become valid. + * + * This value can be changed by executing an operation that calls `updateDelay`. + */ + function getMinDelay() public view virtual returns (uint256 duration) { + return _minDelay; + } + + /** + * @dev Returns the identifier of an operation containing a single + * transaction. + */ + function hashOperation( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(target, value, data, predecessor, salt)); + } + + /** + * @dev Returns the identifier of an operation containing a batch of + * transactions. + */ + function hashOperationBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata payloads, + bytes32 predecessor, + bytes32 salt + ) public pure virtual returns (bytes32 hash) { + return keccak256(abi.encode(targets, values, payloads, predecessor, salt)); + } + + /** + * @dev Schedule an operation containing a single transaction. + * + * Emits a {CallScheduled} event. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function schedule( + address target, + uint256 value, + bytes calldata data, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRole(PROPOSER_ROLE) { + bytes32 id = hashOperation(target, value, data, predecessor, salt); + _schedule(id, delay); + emit CallScheduled(id, 0, target, value, data, predecessor, delay); + } + + /** + * @dev Schedule an operation containing a batch of transactions. + * + * Emits one {CallScheduled} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'proposer' role. + */ + function scheduleBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata payloads, + bytes32 predecessor, + bytes32 salt, + uint256 delay + ) public virtual onlyRole(PROPOSER_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == payloads.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt); + _schedule(id, delay); + for (uint256 i = 0; i < targets.length; ++i) { + emit CallScheduled(id, i, targets[i], values[i], payloads[i], predecessor, delay); + } + } + + /** + * @dev Schedule an operation that is to become valid after a given delay. + */ + function _schedule(bytes32 id, uint256 delay) private { + require(!isOperation(id), "TimelockController: operation already scheduled"); + require(delay >= getMinDelay(), "TimelockController: insufficient delay"); + _timestamps[id] = block.timestamp + delay; + } + + /** + * @dev Cancel an operation. + * + * Requirements: + * + * - the caller must have the 'canceller' role. + */ + function cancel(bytes32 id) public virtual onlyRole(CANCELLER_ROLE) { + require(isOperationPending(id), "TimelockController: operation cannot be cancelled"); + delete _timestamps[id]; + + emit Cancelled(id); + } + + /** + * @dev Execute an (ready) operation containing a single transaction. + * + * Emits a {CallExecuted} event. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + // This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending, + // thus any modifications to the operation during reentrancy should be caught. + // slither-disable-next-line reentrancy-eth + function execute( + address target, + uint256 value, + bytes calldata payload, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { + bytes32 id = hashOperation(target, value, payload, predecessor, salt); + + _beforeCall(id, predecessor); + _execute(target, value, payload); + emit CallExecuted(id, 0, target, value, payload); + _afterCall(id); + } + + /** + * @dev Execute an (ready) operation containing a batch of transactions. + * + * Emits one {CallExecuted} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + function executeBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata payloads, + bytes32 predecessor, + bytes32 salt + ) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == payloads.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt); + + _beforeCall(id, predecessor); + for (uint256 i = 0; i < targets.length; ++i) { + address target = targets[i]; + uint256 value = values[i]; + bytes calldata payload = payloads[i]; + _execute(target, value, payload); + emit CallExecuted(id, i, target, value, payload); + } + _afterCall(id); + } + + /** + * @dev Execute an operation's call. + */ + function _execute(address target, uint256 value, bytes calldata data) internal virtual { + (bool success, ) = target.call{value: value}(data); + require(success, "TimelockController: underlying transaction reverted"); + } + + /** + * @dev Checks before execution of an operation's calls. + */ + function _beforeCall(bytes32 id, bytes32 predecessor) private view { + require(isOperationReady(id), "TimelockController: operation is not ready"); + require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency"); + } + + /** + * @dev Checks after execution of an operation's calls. + */ + function _afterCall(bytes32 id) private { + require(isOperationReady(id), "TimelockController: operation is not ready"); + _timestamps[id] = _DONE_TIMESTAMP; + } + + /** + * @dev Changes the minimum timelock duration for future operations. + * + * Emits a {MinDelayChange} event. + * + * Requirements: + * + * - the caller must be the timelock itself. This can only be achieved by scheduling and later executing + * an operation where the timelock is the target and the data is the ABI-encoded call to this function. + */ + function updateDelay(uint256 newDelay) external virtual { + require(msg.sender == address(this), "TimelockController: caller must be timelock"); + emit MinDelayChange(_minDelay, newDelay); + _minDelay = newDelay; + } + + /** + * @dev See {IERC721Receiver-onERC721Received}. + */ + function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } + + /** + * @dev See {IERC1155Receiver-onERC1155Received}. + */ + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + /** + * @dev See {IERC1155Receiver-onERC1155BatchReceived}. + */ + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/compatibility/GovernorCompatibilityBravo.sol b/lib/openzeppelin-contracts/contracts/governance/compatibility/GovernorCompatibilityBravo.sol new file mode 100644 index 0000000..8d74742 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/compatibility/GovernorCompatibilityBravo.sol @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/compatibility/GovernorCompatibilityBravo.sol) + +pragma solidity ^0.8.0; + +import "../../utils/math/SafeCast.sol"; +import "../extensions/IGovernorTimelock.sol"; +import "../Governor.sol"; +import "./IGovernorCompatibilityBravo.sol"; + +/** + * @dev Compatibility layer that implements GovernorBravo compatibility on top of {Governor}. + * + * This compatibility layer includes a voting system and requires a {IGovernorTimelock} compatible module to be added + * through inheritance. It does not include token bindings, nor does it include any variable upgrade patterns. + * + * NOTE: When using this module, you may need to enable the Solidity optimizer to avoid hitting the contract size limit. + * + * _Available since v4.3._ + */ +abstract contract GovernorCompatibilityBravo is IGovernorTimelock, IGovernorCompatibilityBravo, Governor { + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalDetails { + address proposer; + address[] targets; + uint256[] values; + string[] signatures; + bytes[] calldatas; + uint256 forVotes; + uint256 againstVotes; + uint256 abstainVotes; + mapping(address => Receipt) receipts; + bytes32 descriptionHash; + } + + mapping(uint256 => ProposalDetails) private _proposalDetails; + + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=bravo"; + } + + // ============================================== Proposal lifecycle ============================================== + /** + * @dev See {IGovernor-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override(IGovernor, Governor) returns (uint256) { + _storeProposal(_msgSender(), targets, values, new string[](calldatas.length), calldatas, description); + return super.propose(targets, values, calldatas, description); + } + + /** + * @dev See {IGovernorCompatibilityBravo-propose}. + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + _storeProposal(_msgSender(), targets, values, signatures, calldatas, description); + return propose(targets, values, _encodeCalldata(signatures, calldatas), description); + } + + /** + * @dev See {IGovernorCompatibilityBravo-queue}. + */ + function queue(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + queue( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + /** + * @dev See {IGovernorCompatibilityBravo-execute}. + */ + function execute(uint256 proposalId) public payable virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + execute( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + function cancel(uint256 proposalId) public virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + + require( + _msgSender() == details.proposer || getVotes(details.proposer, block.number - 1) < proposalThreshold(), + "GovernorBravo: proposer above threshold" + ); + + _cancel( + details.targets, + details.values, + _encodeCalldata(details.signatures, details.calldatas), + details.descriptionHash + ); + } + + /** + * @dev Encodes calldatas with optional function signature. + */ + function _encodeCalldata( + string[] memory signatures, + bytes[] memory calldatas + ) private pure returns (bytes[] memory) { + bytes[] memory fullcalldatas = new bytes[](calldatas.length); + + for (uint256 i = 0; i < signatures.length; ++i) { + fullcalldatas[i] = bytes(signatures[i]).length == 0 + ? calldatas[i] + : abi.encodePacked(bytes4(keccak256(bytes(signatures[i]))), calldatas[i]); + } + + return fullcalldatas; + } + + /** + * @dev Store proposal metadata for later lookup + */ + function _storeProposal( + address proposer, + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) private { + bytes32 descriptionHash = keccak256(bytes(description)); + uint256 proposalId = hashProposal(targets, values, _encodeCalldata(signatures, calldatas), descriptionHash); + + ProposalDetails storage details = _proposalDetails[proposalId]; + if (details.descriptionHash == bytes32(0)) { + details.proposer = proposer; + details.targets = targets; + details.values = values; + details.signatures = signatures; + details.calldatas = calldatas; + details.descriptionHash = descriptionHash; + } + } + + // ==================================================== Views ===================================================== + /** + * @dev See {IGovernorCompatibilityBravo-proposals}. + */ + function proposals( + uint256 proposalId + ) + public + view + virtual + override + returns ( + uint256 id, + address proposer, + uint256 eta, + uint256 startBlock, + uint256 endBlock, + uint256 forVotes, + uint256 againstVotes, + uint256 abstainVotes, + bool canceled, + bool executed + ) + { + id = proposalId; + eta = proposalEta(proposalId); + startBlock = proposalSnapshot(proposalId); + endBlock = proposalDeadline(proposalId); + + ProposalDetails storage details = _proposalDetails[proposalId]; + proposer = details.proposer; + forVotes = details.forVotes; + againstVotes = details.againstVotes; + abstainVotes = details.abstainVotes; + + ProposalState status = state(proposalId); + canceled = status == ProposalState.Canceled; + executed = status == ProposalState.Executed; + } + + /** + * @dev See {IGovernorCompatibilityBravo-getActions}. + */ + function getActions( + uint256 proposalId + ) + public + view + virtual + override + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ) + { + ProposalDetails storage details = _proposalDetails[proposalId]; + return (details.targets, details.values, details.signatures, details.calldatas); + } + + /** + * @dev See {IGovernorCompatibilityBravo-getReceipt}. + */ + function getReceipt(uint256 proposalId, address voter) public view virtual override returns (Receipt memory) { + return _proposalDetails[proposalId].receipts[voter]; + } + + /** + * @dev See {IGovernorCompatibilityBravo-quorumVotes}. + */ + function quorumVotes() public view virtual override returns (uint256) { + return quorum(block.number - 1); + } + + // ==================================================== Voting ==================================================== + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { + return _proposalDetails[proposalId].receipts[account].hasVoted; + } + + /** + * @dev See {Governor-_quorumReached}. In this module, only forVotes count toward the quorum. + */ + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + ProposalDetails storage details = _proposalDetails[proposalId]; + return quorum(proposalSnapshot(proposalId)) <= details.forVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + ProposalDetails storage details = _proposalDetails[proposalId]; + return details.forVotes > details.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows Governor Bravo. + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory // params + ) internal virtual override { + ProposalDetails storage details = _proposalDetails[proposalId]; + Receipt storage receipt = details.receipts[account]; + + require(!receipt.hasVoted, "GovernorCompatibilityBravo: vote already cast"); + receipt.hasVoted = true; + receipt.support = support; + receipt.votes = SafeCast.toUint96(weight); + + if (support == uint8(VoteType.Against)) { + details.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + details.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + details.abstainVotes += weight; + } else { + revert("GovernorCompatibilityBravo: invalid vote type"); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol b/lib/openzeppelin-contracts/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol new file mode 100644 index 0000000..d1ec033 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/compatibility/IGovernorCompatibilityBravo.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/compatibility/IGovernorCompatibilityBravo.sol) + +pragma solidity ^0.8.0; + +import "../IGovernor.sol"; + +/** + * @dev Interface extension that adds missing functions to the {Governor} core to provide `GovernorBravo` compatibility. + * + * _Available since v4.3._ + */ +abstract contract IGovernorCompatibilityBravo is IGovernor { + /** + * @dev Proposal structure from Compound Governor Bravo. Not actually used by the compatibility layer, as + * {{proposal}} returns a very different structure. + */ + struct Proposal { + uint256 id; + address proposer; + uint256 eta; + address[] targets; + uint256[] values; + string[] signatures; + bytes[] calldatas; + uint256 startBlock; + uint256 endBlock; + uint256 forVotes; + uint256 againstVotes; + uint256 abstainVotes; + bool canceled; + bool executed; + mapping(address => Receipt) receipts; + } + + /** + * @dev Receipt structure from Compound Governor Bravo + */ + struct Receipt { + bool hasVoted; + uint8 support; + uint96 votes; + } + + /** + * @dev Part of the Governor Bravo's interface. + */ + function quorumVotes() public view virtual returns (uint256); + + /** + * @dev Part of the Governor Bravo's interface: _"The official record of all proposals ever proposed"_. + */ + function proposals( + uint256 + ) + public + view + virtual + returns ( + uint256 id, + address proposer, + uint256 eta, + uint256 startBlock, + uint256 endBlock, + uint256 forVotes, + uint256 againstVotes, + uint256 abstainVotes, + bool canceled, + bool executed + ); + + /** + * @dev Part of the Governor Bravo's interface: _"Function used to propose a new proposal"_. + */ + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) public virtual returns (uint256); + + /** + * @dev Part of the Governor Bravo's interface: _"Queues a proposal of state succeeded"_. + */ + function queue(uint256 proposalId) public virtual; + + /** + * @dev Part of the Governor Bravo's interface: _"Executes a queued proposal if eta has passed"_. + */ + function execute(uint256 proposalId) public payable virtual; + + /** + * @dev Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold. + */ + function cancel(uint256 proposalId) public virtual; + + /** + * @dev Part of the Governor Bravo's interface: _"Gets actions of a proposal"_. + */ + function getActions( + uint256 proposalId + ) + public + view + virtual + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ); + + /** + * @dev Part of the Governor Bravo's interface: _"Gets the receipt for a voter on a given proposal"_. + */ + function getReceipt(uint256 proposalId, address voter) public view virtual returns (Receipt memory); +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingSimple.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingSimple.sol new file mode 100644 index 0000000..f3eea9d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingSimple.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/extensions/GovernorCountingSimple.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for simple, 3 options, vote counting. + * + * _Available since v4.3._ + */ +abstract contract GovernorCountingSimple is Governor { + /** + * @dev Supported vote types. Matches Governor Bravo ordering. + */ + enum VoteType { + Against, + For, + Abstain + } + + struct ProposalVote { + uint256 againstVotes; + uint256 forVotes; + uint256 abstainVotes; + mapping(address => bool) hasVoted; + } + + mapping(uint256 => ProposalVote) private _proposalVotes; + + /** + * @dev See {IGovernor-COUNTING_MODE}. + */ + // solhint-disable-next-line func-name-mixedcase + function COUNTING_MODE() public pure virtual override returns (string memory) { + return "support=bravo&quorum=for,abstain"; + } + + /** + * @dev See {IGovernor-hasVoted}. + */ + function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) { + return _proposalVotes[proposalId].hasVoted[account]; + } + + /** + * @dev Accessor to the internal vote counts. + */ + function proposalVotes( + uint256 proposalId + ) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes) { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes); + } + + /** + * @dev See {Governor-_quorumReached}. + */ + function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + return quorum(proposalSnapshot(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes; + } + + /** + * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes. + */ + function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + return proposalVote.forVotes > proposalVote.againstVotes; + } + + /** + * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo). + */ + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory // params + ) internal virtual override { + ProposalVote storage proposalVote = _proposalVotes[proposalId]; + + require(!proposalVote.hasVoted[account], "GovernorVotingSimple: vote already cast"); + proposalVote.hasVoted[account] = true; + + if (support == uint8(VoteType.Against)) { + proposalVote.againstVotes += weight; + } else if (support == uint8(VoteType.For)) { + proposalVote.forVotes += weight; + } else if (support == uint8(VoteType.Abstain)) { + proposalVote.abstainVotes += weight; + } else { + revert("GovernorVotingSimple: invalid value for enum VoteType"); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorPreventLateQuorum.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorPreventLateQuorum.sol new file mode 100644 index 0000000..a26bbf0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorPreventLateQuorum.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorPreventLateQuorum.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../../utils/math/Math.sol"; + +/** + * @dev A module that ensures there is a minimum voting period after quorum is reached. This prevents a large voter from + * swaying a vote and triggering quorum at the last minute, by ensuring there is always time for other voters to react + * and try to oppose the decision. + * + * If a vote causes quorum to be reached, the proposal's voting period may be extended so that it does not end before at + * least a given number of blocks have passed (the "vote extension" parameter). This parameter can be set by the + * governance executor (e.g. through a governance proposal). + * + * _Available since v4.5._ + */ +abstract contract GovernorPreventLateQuorum is Governor { + using SafeCast for uint256; + using Timers for Timers.BlockNumber; + + uint64 private _voteExtension; + mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; + + /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. + event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); + + /// @dev Emitted when the {lateQuorumVoteExtension} parameter is changed. + event LateQuorumVoteExtensionSet(uint64 oldVoteExtension, uint64 newVoteExtension); + + /** + * @dev Initializes the vote extension parameter: the number of blocks that are required to pass since a proposal + * reaches quorum until its voting period ends. If necessary the voting period will be extended beyond the one set + * at proposal creation. + */ + constructor(uint64 initialVoteExtension) { + _setLateQuorumVoteExtension(initialVoteExtension); + } + + /** + * @dev Returns the proposal deadline, which may have been extended beyond that set at proposal creation, if the + * proposal reached quorum late in the voting period. See {Governor-proposalDeadline}. + */ + function proposalDeadline(uint256 proposalId) public view virtual override returns (uint256) { + return Math.max(super.proposalDeadline(proposalId), _extendedDeadlines[proposalId].getDeadline()); + } + + /** + * @dev Casts a vote and detects if it caused quorum to be reached, potentially extending the voting period. See + * {Governor-_castVote}. + * + * May emit a {ProposalExtended} event. + */ + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal virtual override returns (uint256) { + uint256 result = super._castVote(proposalId, account, support, reason, params); + + Timers.BlockNumber storage extendedDeadline = _extendedDeadlines[proposalId]; + + if (extendedDeadline.isUnset() && _quorumReached(proposalId)) { + uint64 extendedDeadlineValue = block.number.toUint64() + lateQuorumVoteExtension(); + + if (extendedDeadlineValue > proposalDeadline(proposalId)) { + emit ProposalExtended(proposalId, extendedDeadlineValue); + } + + extendedDeadline.setDeadline(extendedDeadlineValue); + } + + return result; + } + + /** + * @dev Returns the current value of the vote extension parameter: the number of blocks that are required to pass + * from the time a proposal reaches quorum until its voting period ends. + */ + function lateQuorumVoteExtension() public view virtual returns (uint64) { + return _voteExtension; + } + + /** + * @dev Changes the {lateQuorumVoteExtension}. This operation can only be performed by the governance executor, + * generally through a governance proposal. + * + * Emits a {LateQuorumVoteExtensionSet} event. + */ + function setLateQuorumVoteExtension(uint64 newVoteExtension) public virtual onlyGovernance { + _setLateQuorumVoteExtension(newVoteExtension); + } + + /** + * @dev Changes the {lateQuorumVoteExtension}. This is an internal function that can be exposed in a public function + * like {setLateQuorumVoteExtension} if another access control mechanism is needed. + * + * Emits a {LateQuorumVoteExtensionSet} event. + */ + function _setLateQuorumVoteExtension(uint64 newVoteExtension) internal virtual { + emit LateQuorumVoteExtensionSet(_voteExtension, newVoteExtension); + _voteExtension = newVoteExtension; + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorProposalThreshold.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorProposalThreshold.sol new file mode 100644 index 0000000..3feebac --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorProposalThreshold.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorProposalThreshold.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for proposal restriction to token holders with a minimum balance. + * + * _Available since v4.3._ + * _Deprecated since v4.4._ + */ +abstract contract GovernorProposalThreshold is Governor { + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public virtual override returns (uint256) { + return super.propose(targets, values, calldatas, description); + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorSettings.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorSettings.sol new file mode 100644 index 0000000..527f41c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorSettings.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/GovernorSettings.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; + +/** + * @dev Extension of {Governor} for settings updatable through governance. + * + * _Available since v4.4._ + */ +abstract contract GovernorSettings is Governor { + uint256 private _votingDelay; + uint256 private _votingPeriod; + uint256 private _proposalThreshold; + + event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay); + event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod); + event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold); + + /** + * @dev Initialize the governance parameters. + */ + constructor(uint256 initialVotingDelay, uint256 initialVotingPeriod, uint256 initialProposalThreshold) { + _setVotingDelay(initialVotingDelay); + _setVotingPeriod(initialVotingPeriod); + _setProposalThreshold(initialProposalThreshold); + } + + /** + * @dev See {IGovernor-votingDelay}. + */ + function votingDelay() public view virtual override returns (uint256) { + return _votingDelay; + } + + /** + * @dev See {IGovernor-votingPeriod}. + */ + function votingPeriod() public view virtual override returns (uint256) { + return _votingPeriod; + } + + /** + * @dev See {Governor-proposalThreshold}. + */ + function proposalThreshold() public view virtual override returns (uint256) { + return _proposalThreshold; + } + + /** + * @dev Update the voting delay. This operation can only be performed through a governance proposal. + * + * Emits a {VotingDelaySet} event. + */ + function setVotingDelay(uint256 newVotingDelay) public virtual onlyGovernance { + _setVotingDelay(newVotingDelay); + } + + /** + * @dev Update the voting period. This operation can only be performed through a governance proposal. + * + * Emits a {VotingPeriodSet} event. + */ + function setVotingPeriod(uint256 newVotingPeriod) public virtual onlyGovernance { + _setVotingPeriod(newVotingPeriod); + } + + /** + * @dev Update the proposal threshold. This operation can only be performed through a governance proposal. + * + * Emits a {ProposalThresholdSet} event. + */ + function setProposalThreshold(uint256 newProposalThreshold) public virtual onlyGovernance { + _setProposalThreshold(newProposalThreshold); + } + + /** + * @dev Internal setter for the voting delay. + * + * Emits a {VotingDelaySet} event. + */ + function _setVotingDelay(uint256 newVotingDelay) internal virtual { + emit VotingDelaySet(_votingDelay, newVotingDelay); + _votingDelay = newVotingDelay; + } + + /** + * @dev Internal setter for the voting period. + * + * Emits a {VotingPeriodSet} event. + */ + function _setVotingPeriod(uint256 newVotingPeriod) internal virtual { + // voting period must be at least one block long + require(newVotingPeriod > 0, "GovernorSettings: voting period too low"); + emit VotingPeriodSet(_votingPeriod, newVotingPeriod); + _votingPeriod = newVotingPeriod; + } + + /** + * @dev Internal setter for the proposal threshold. + * + * Emits a {ProposalThresholdSet} event. + */ + function _setProposalThreshold(uint256 newProposalThreshold) internal virtual { + emit ProposalThresholdSet(_proposalThreshold, newProposalThreshold); + _proposalThreshold = newProposalThreshold; + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockCompound.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockCompound.sol new file mode 100644 index 0000000..2fa539e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockCompound.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorTimelockCompound.sol) + +pragma solidity ^0.8.0; + +import "./IGovernorTimelock.sol"; +import "../Governor.sol"; +import "../../utils/math/SafeCast.sol"; +import "../../vendor/compound/ICompoundTimelock.sol"; + +/** + * @dev Extension of {Governor} that binds the execution process to a Compound Timelock. This adds a delay, enforced by + * the external timelock to all successful proposal (in addition to the voting duration). The {Governor} needs to be + * the admin of the timelock for any operation to be performed. A public, unrestricted, + * {GovernorTimelockCompound-__acceptAdmin} is available to accept ownership of the timelock. + * + * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, + * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be + * inaccessible. + * + * _Available since v4.3._ + */ +abstract contract GovernorTimelockCompound is IGovernorTimelock, Governor { + using SafeCast for uint256; + using Timers for Timers.Timestamp; + + struct ProposalTimelock { + Timers.Timestamp timer; + } + + ICompoundTimelock private _timelock; + + mapping(uint256 => ProposalTimelock) private _proposalTimelocks; + + /** + * @dev Emitted when the timelock controller used for proposal execution is modified. + */ + event TimelockChange(address oldTimelock, address newTimelock); + + /** + * @dev Set the timelock. + */ + constructor(ICompoundTimelock timelockAddress) { + _updateTimelock(timelockAddress); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { + return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Overridden version of the {Governor-state} function with added support for the `Queued` and `Expired` status. + */ + function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { + ProposalState status = super.state(proposalId); + + if (status != ProposalState.Succeeded) { + return status; + } + + uint256 eta = proposalEta(proposalId); + if (eta == 0) { + return status; + } else if (block.timestamp >= eta + _timelock.GRACE_PERIOD()) { + return ProposalState.Expired; + } else { + return ProposalState.Queued; + } + } + + /** + * @dev Public accessor to check the address of the timelock + */ + function timelock() public view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public accessor to check the eta of a queued proposal + */ + function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { + return _proposalTimelocks[proposalId].timer.getDeadline(); + } + + /** + * @dev Function to queue a proposal to the timelock. + */ + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); + + uint256 eta = block.timestamp + _timelock.delay(); + _proposalTimelocks[proposalId].timer.setDeadline(eta.toUint64()); + for (uint256 i = 0; i < targets.length; ++i) { + require( + !_timelock.queuedTransactions(keccak256(abi.encode(targets[i], values[i], "", calldatas[i], eta))), + "GovernorTimelockCompound: identical proposal action already queued" + ); + _timelock.queueTransaction(targets[i], values[i], "", calldatas[i], eta); + } + + emit ProposalQueued(proposalId, eta); + + return proposalId; + } + + /** + * @dev Overridden execute function that run the already queued proposal through the timelock. + */ + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 /*descriptionHash*/ + ) internal virtual override { + uint256 eta = proposalEta(proposalId); + require(eta > 0, "GovernorTimelockCompound: proposal not yet queued"); + Address.sendValue(payable(_timelock), msg.value); + for (uint256 i = 0; i < targets.length; ++i) { + _timelock.executeTransaction(targets[i], values[i], "", calldatas[i], eta); + } + } + + /** + * @dev Overridden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already + * been queued. + */ + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override returns (uint256) { + uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); + + uint256 eta = proposalEta(proposalId); + if (eta > 0) { + for (uint256 i = 0; i < targets.length; ++i) { + _timelock.cancelTransaction(targets[i], values[i], "", calldatas[i], eta); + } + _proposalTimelocks[proposalId].timer.reset(); + } + + return proposalId; + } + + /** + * @dev Address through which the governor executes action. In this case, the timelock. + */ + function _executor() internal view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Accept admin right over the timelock. + */ + // solhint-disable-next-line private-vars-leading-underscore + function __acceptAdmin() public { + _timelock.acceptAdmin(); + } + + /** + * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates + * must be proposed, scheduled, and executed through governance proposals. + * + * For security reasons, the timelock must be handed over to another admin before setting up a new one. The two + * operations (hand over the timelock) and do the update can be batched in a single proposal. + * + * Note that if the timelock admin has been handed over in a previous operation, we refuse updates made through the + * timelock if admin of the timelock has already been accepted and the operation is executed outside the scope of + * governance. + + * CAUTION: It is not recommended to change the timelock while there are other queued governance proposals. + */ + function updateTimelock(ICompoundTimelock newTimelock) external virtual onlyGovernance { + _updateTimelock(newTimelock); + } + + function _updateTimelock(ICompoundTimelock newTimelock) private { + emit TimelockChange(address(_timelock), address(newTimelock)); + _timelock = newTimelock; + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockControl.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockControl.sol new file mode 100644 index 0000000..6aa2556 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockControl.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorTimelockControl.sol) + +pragma solidity ^0.8.0; + +import "./IGovernorTimelock.sol"; +import "../Governor.sol"; +import "../TimelockController.sol"; + +/** + * @dev Extension of {Governor} that binds the execution process to an instance of {TimelockController}. This adds a + * delay, enforced by the {TimelockController} to all successful proposal (in addition to the voting duration). The + * {Governor} needs the proposer (and ideally the executor) roles for the {Governor} to work properly. + * + * Using this model means the proposal will be operated by the {TimelockController} and not by the {Governor}. Thus, + * the assets and permissions must be attached to the {TimelockController}. Any asset sent to the {Governor} will be + * inaccessible. + * + * WARNING: Setting up the TimelockController to have additional proposers besides the governor is very risky, as it + * grants them powers that they must be trusted or known not to use: 1) {onlyGovernance} functions like {relay} are + * available to them through the timelock, and 2) approved governance proposals can be blocked by them, effectively + * executing a Denial of Service attack. This risk will be mitigated in a future release. + * + * _Available since v4.3._ + */ +abstract contract GovernorTimelockControl is IGovernorTimelock, Governor { + TimelockController private _timelock; + mapping(uint256 => bytes32) private _timelockIds; + + /** + * @dev Emitted when the timelock controller used for proposal execution is modified. + */ + event TimelockChange(address oldTimelock, address newTimelock); + + /** + * @dev Set the timelock. + */ + constructor(TimelockController timelockAddress) { + _updateTimelock(timelockAddress); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, Governor) returns (bool) { + return interfaceId == type(IGovernorTimelock).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev Overridden version of the {Governor-state} function with added support for the `Queued` status. + */ + function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) { + ProposalState status = super.state(proposalId); + + if (status != ProposalState.Succeeded) { + return status; + } + + // core tracks execution, so we just have to check if successful proposal have been queued. + bytes32 queueid = _timelockIds[proposalId]; + if (queueid == bytes32(0)) { + return status; + } else if (_timelock.isOperationDone(queueid)) { + return ProposalState.Executed; + } else if (_timelock.isOperationPending(queueid)) { + return ProposalState.Queued; + } else { + return ProposalState.Canceled; + } + } + + /** + * @dev Public accessor to check the address of the timelock + */ + function timelock() public view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public accessor to check the eta of a queued proposal + */ + function proposalEta(uint256 proposalId) public view virtual override returns (uint256) { + uint256 eta = _timelock.getTimestamp(_timelockIds[proposalId]); + return eta == 1 ? 0 : eta; // _DONE_TIMESTAMP (1) should be replaced with a 0 value + } + + /** + * @dev Function to queue a proposal to the timelock. + */ + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual override returns (uint256) { + uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash); + + require(state(proposalId) == ProposalState.Succeeded, "Governor: proposal not successful"); + + uint256 delay = _timelock.getMinDelay(); + _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); + _timelock.scheduleBatch(targets, values, calldatas, 0, descriptionHash, delay); + + emit ProposalQueued(proposalId, block.timestamp + delay); + + return proposalId; + } + + /** + * @dev Overridden execute function that run the already queued proposal through the timelock. + */ + function _execute( + uint256 /* proposalId */, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override { + _timelock.executeBatch{value: msg.value}(targets, values, calldatas, 0, descriptionHash); + } + + /** + * @dev Overridden version of the {Governor-_cancel} function to cancel the timelocked proposal if it as already + * been queued. + */ + // This function can reenter through the external call to the timelock, but we assume the timelock is trusted and + // well behaved (according to TimelockController) and this will not happen. + // slither-disable-next-line reentrancy-no-eth + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal virtual override returns (uint256) { + uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash); + + if (_timelockIds[proposalId] != 0) { + _timelock.cancel(_timelockIds[proposalId]); + delete _timelockIds[proposalId]; + } + + return proposalId; + } + + /** + * @dev Address through which the governor executes action. In this case, the timelock. + */ + function _executor() internal view virtual override returns (address) { + return address(_timelock); + } + + /** + * @dev Public endpoint to update the underlying timelock instance. Restricted to the timelock itself, so updates + * must be proposed, scheduled, and executed through governance proposals. + * + * CAUTION: It is not recommended to change the timelock while there are other queued governance proposals. + */ + function updateTimelock(TimelockController newTimelock) external virtual onlyGovernance { + _updateTimelock(newTimelock); + } + + function _updateTimelock(TimelockController newTimelock) private { + emit TimelockChange(address(_timelock), address(newTimelock)); + _timelock = newTimelock; + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotes.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotes.sol new file mode 100644 index 0000000..b328c25 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotes.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorVotes.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../utils/IVotes.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token, or since v4.5 an {ERC721Votes} token. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotes is Governor { + IVotes public immutable token; + + constructor(IVotes tokenAddress) { + token = tokenAddress; + } + + /** + * Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}). + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory /*params*/ + ) internal view virtual override returns (uint256) { + return token.getPastVotes(account, blockNumber); + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesComp.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesComp.sol new file mode 100644 index 0000000..8c4a1f8 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesComp.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorVotesComp.sol) + +pragma solidity ^0.8.0; + +import "../Governor.sol"; +import "../../token/ERC20/extensions/ERC20VotesComp.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from a Comp token. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotesComp is Governor { + ERC20VotesComp public immutable token; + + constructor(ERC20VotesComp token_) { + token = token_; + } + + /** + * Read the voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}). + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory /*params*/ + ) internal view virtual override returns (uint256) { + return token.getPriorVotes(account, blockNumber); + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesQuorumFraction.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesQuorumFraction.sol new file mode 100644 index 0000000..8efefce --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesQuorumFraction.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/extensions/GovernorVotesQuorumFraction.sol) + +pragma solidity ^0.8.0; + +import "./GovernorVotes.sol"; +import "../../utils/Checkpoints.sol"; +import "../../utils/math/SafeCast.sol"; + +/** + * @dev Extension of {Governor} for voting weight extraction from an {ERC20Votes} token and a quorum expressed as a + * fraction of the total supply. + * + * _Available since v4.3._ + */ +abstract contract GovernorVotesQuorumFraction is GovernorVotes { + using Checkpoints for Checkpoints.History; + + uint256 private _quorumNumerator; // DEPRECATED + Checkpoints.History private _quorumNumeratorHistory; + + event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator); + + /** + * @dev Initialize quorum as a fraction of the token's total supply. + * + * The fraction is specified as `numerator / denominator`. By default the denominator is 100, so quorum is + * specified as a percent: a numerator of 10 corresponds to quorum being 10% of total supply. The denominator can be + * customized by overriding {quorumDenominator}. + */ + constructor(uint256 quorumNumeratorValue) { + _updateQuorumNumerator(quorumNumeratorValue); + } + + /** + * @dev Returns the current quorum numerator. See {quorumDenominator}. + */ + function quorumNumerator() public view virtual returns (uint256) { + return _quorumNumeratorHistory._checkpoints.length == 0 ? _quorumNumerator : _quorumNumeratorHistory.latest(); + } + + /** + * @dev Returns the quorum numerator at a specific block number. See {quorumDenominator}. + */ + function quorumNumerator(uint256 blockNumber) public view virtual returns (uint256) { + // If history is empty, fallback to old storage + uint256 length = _quorumNumeratorHistory._checkpoints.length; + if (length == 0) { + return _quorumNumerator; + } + + // Optimistic search, check the latest checkpoint + Checkpoints.Checkpoint memory latest = _quorumNumeratorHistory._checkpoints[length - 1]; + if (latest._blockNumber <= blockNumber) { + return latest._value; + } + + // Otherwise, do the binary search + return _quorumNumeratorHistory.getAtBlock(blockNumber); + } + + /** + * @dev Returns the quorum denominator. Defaults to 100, but may be overridden. + */ + function quorumDenominator() public view virtual returns (uint256) { + return 100; + } + + /** + * @dev Returns the quorum for a block number, in terms of number of votes: `supply * numerator / denominator`. + */ + function quorum(uint256 blockNumber) public view virtual override returns (uint256) { + return (token.getPastTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / quorumDenominator(); + } + + /** + * @dev Changes the quorum numerator. + * + * Emits a {QuorumNumeratorUpdated} event. + * + * Requirements: + * + * - Must be called through a governance proposal. + * - New numerator must be smaller or equal to the denominator. + */ + function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual onlyGovernance { + _updateQuorumNumerator(newQuorumNumerator); + } + + /** + * @dev Changes the quorum numerator. + * + * Emits a {QuorumNumeratorUpdated} event. + * + * Requirements: + * + * - New numerator must be smaller or equal to the denominator. + */ + function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual { + require( + newQuorumNumerator <= quorumDenominator(), + "GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator" + ); + + uint256 oldQuorumNumerator = quorumNumerator(); + + // Make sure we keep track of the original numerator in contracts upgraded from a version without checkpoints. + if (oldQuorumNumerator != 0 && _quorumNumeratorHistory._checkpoints.length == 0) { + _quorumNumeratorHistory._checkpoints.push( + Checkpoints.Checkpoint({_blockNumber: 0, _value: SafeCast.toUint224(oldQuorumNumerator)}) + ); + } + + // Set new quorum for future proposals + _quorumNumeratorHistory.push(newQuorumNumerator); + + emit QuorumNumeratorUpdated(oldQuorumNumerator, newQuorumNumerator); + } +} diff --git a/lib/openzeppelin-contracts/contracts/governance/extensions/IGovernorTimelock.sol b/lib/openzeppelin-contracts/contracts/governance/extensions/IGovernorTimelock.sol new file mode 100644 index 0000000..40402f6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/extensions/IGovernorTimelock.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (governance/extensions/IGovernorTimelock.sol) + +pragma solidity ^0.8.0; + +import "../IGovernor.sol"; + +/** + * @dev Extension of the {IGovernor} for timelock supporting modules. + * + * _Available since v4.3._ + */ +abstract contract IGovernorTimelock is IGovernor { + event ProposalQueued(uint256 proposalId, uint256 eta); + + function timelock() public view virtual returns (address); + + function proposalEta(uint256 proposalId) public view virtual returns (uint256); + + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual returns (uint256 proposalId); +} diff --git a/lib/openzeppelin-contracts/contracts/governance/utils/IVotes.sol b/lib/openzeppelin-contracts/contracts/governance/utils/IVotes.sol new file mode 100644 index 0000000..0bef3f9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/utils/IVotes.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol) +pragma solidity ^0.8.0; + +/** + * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. + * + * _Available since v4.5._ + */ +interface IVotes { + /** + * @dev Emitted when an account changes their delegate. + */ + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); + + /** + * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) external view returns (uint256); + + /** + * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). + */ + function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); + + /** + * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + */ + function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) external view returns (address); + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) external; + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external; +} diff --git a/lib/openzeppelin-contracts/contracts/governance/utils/Votes.sol b/lib/openzeppelin-contracts/contracts/governance/utils/Votes.sol new file mode 100644 index 0000000..2eecf32 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/governance/utils/Votes.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (governance/utils/Votes.sol) +pragma solidity ^0.8.0; + +import "../../utils/Context.sol"; +import "../../utils/Counters.sol"; +import "../../utils/Checkpoints.sol"; +import "../../utils/cryptography/EIP712.sol"; +import "./IVotes.sol"; + +/** + * @dev This is a base abstract contract that tracks voting units, which are a measure of voting power that can be + * transferred, and provides a system of vote delegation, where an account can delegate its voting units to a sort of + * "representative" that will pool delegated voting units from different accounts and can then use it to vote in + * decisions. In fact, voting units _must_ be delegated in order to count as actual votes, and an account has to + * delegate those votes to itself if it wishes to participate in decisions and does not have a trusted representative. + * + * This contract is often combined with a token contract such that voting units correspond to token units. For an + * example, see {ERC721Votes}. + * + * The full history of delegate votes is tracked on-chain so that governance protocols can consider votes as distributed + * at a particular block number to protect against flash loans and double voting. The opt-in delegate system makes the + * cost of this history tracking optional. + * + * When using this module the derived contract must implement {_getVotingUnits} (for example, make it return + * {ERC721-balanceOf}), and can use {_transferVotingUnits} to track a change in the distribution of those units (in the + * previous example, it would be included in {ERC721-_beforeTokenTransfer}). + * + * _Available since v4.5._ + */ +abstract contract Votes is IVotes, Context, EIP712 { + using Checkpoints for Checkpoints.History; + using Counters for Counters.Counter; + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegation; + mapping(address => Checkpoints.History) private _delegateCheckpoints; + Checkpoints.History private _totalCheckpoints; + + mapping(address => Counters.Counter) private _nonces; + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) public view virtual override returns (uint256) { + return _delegateCheckpoints[account].latest(); + } + + /** + * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`). + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + return _delegateCheckpoints[account].getAtProbablyRecentBlock(blockNumber); + } + + /** + * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`). + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "Votes: block not yet mined"); + return _totalCheckpoints.getAtProbablyRecentBlock(blockNumber); + } + + /** + * @dev Returns the current total supply of votes. + */ + function _getTotalSupply() internal view virtual returns (uint256) { + return _totalCheckpoints.latest(); + } + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) public view virtual override returns (address) { + return _delegation[account]; + } + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual override { + address account = _msgSender(); + _delegate(account, delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= expiry, "Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Delegate all of `account`'s voting units to `delegatee`. + * + * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. + */ + function _delegate(address account, address delegatee) internal virtual { + address oldDelegate = delegates(account); + _delegation[account] = delegatee; + + emit DelegateChanged(account, oldDelegate, delegatee); + _moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account)); + } + + /** + * @dev Transfers, mints, or burns voting units. To register a mint, `from` should be zero. To register a burn, `to` + * should be zero. Total supply of voting units will be adjusted with mints and burns. + */ + function _transferVotingUnits(address from, address to, uint256 amount) internal virtual { + if (from == address(0)) { + _totalCheckpoints.push(_add, amount); + } + if (to == address(0)) { + _totalCheckpoints.push(_subtract, amount); + } + _moveDelegateVotes(delegates(from), delegates(to), amount); + } + + /** + * @dev Moves delegated votes from one delegate to another. + */ + function _moveDelegateVotes(address from, address to, uint256 amount) private { + if (from != to && amount > 0) { + if (from != address(0)) { + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount); + emit DelegateVotesChanged(from, oldValue, newValue); + } + if (to != address(0)) { + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount); + emit DelegateVotesChanged(to, oldValue, newValue); + } + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } + + /** + * @dev Consumes a nonce. + * + * Returns the current value and increments nonce. + */ + function _useNonce(address owner) internal virtual returns (uint256 current) { + Counters.Counter storage nonce = _nonces[owner]; + current = nonce.current(); + nonce.increment(); + } + + /** + * @dev Returns an address nonce. + */ + function nonces(address owner) public view virtual returns (uint256) { + return _nonces[owner].current(); + } + + /** + * @dev Returns the contract's {EIP712} domain separator. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @dev Must return the voting units held by an account. + */ + function _getVotingUnits(address) internal view virtual returns (uint256); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1155.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1155.sol new file mode 100644 index 0000000..f891132 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1155.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1155MetadataURI.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1155MetadataURI.sol new file mode 100644 index 0000000..2aa885f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1155MetadataURI.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155MetadataURI.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/extensions/IERC1155MetadataURI.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1155Receiver.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1155Receiver.sol new file mode 100644 index 0000000..a6d4ead --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1155Receiver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155Receiver.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1271.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1271.sol new file mode 100644 index 0000000..5ec44c7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1271.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC1271 standard signature validation method for + * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. + * + * _Available since v4.1._ + */ +interface IERC1271 { + /** + * @dev Should return whether the signature provided is valid for the provided data + * @param hash Hash of the data to be signed + * @param signature Signature byte array associated with _data + */ + function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol new file mode 100644 index 0000000..1a8dc79 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./IERC165.sol"; + +interface IERC1363 is IERC165, IERC20 { + /* + * Note: the ERC-165 identifier for this interface is 0x4bbee2df. + * 0x4bbee2df === + * bytes4(keccak256('transferAndCall(address,uint256)')) ^ + * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) + */ + + /* + * Note: the ERC-165 identifier for this interface is 0xfb9ec8ce. + * 0xfb9ec8ce === + * bytes4(keccak256('approveAndCall(address,uint256)')) ^ + * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) + */ + + /** + * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferAndCall(address to, uint256 value) external returns (bool); + + /** + * @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool); + + /** + * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferFromAndCall(address from, address to, uint256 value) external returns (bool); + + /** + * @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool); + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + */ + function approveAndCall(address spender, uint256 value) external returns (bool); + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format, sent in call to `spender` + */ + function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Receiver.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Receiver.sol new file mode 100644 index 0000000..bc5eadd --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Receiver.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363Receiver.sol) + +pragma solidity ^0.8.0; + +interface IERC1363Receiver { + /* + * Note: the ERC-165 identifier for this interface is 0x88a7ca5c. + * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)")) + */ + + /** + * @notice Handle the receipt of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the + * transfer. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function + * @param from address The address which are token transferred from + * @param value uint256 The amount of tokens transferred + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` + * unless throwing + */ + function onTransferReceived( + address operator, + address from, + uint256 value, + bytes memory data + ) external returns (bytes4); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Spender.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Spender.sol new file mode 100644 index 0000000..28775e1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1363Spender.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1363Spender.sol) + +pragma solidity ^0.8.0; + +interface IERC1363Spender { + /* + * Note: the ERC-165 identifier for this interface is 0x7b04a2d0. + * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)")) + */ + + /** + * @notice Handle the approval of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after an `approve`. This function MAY throw to revert and reject the + * approval. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param owner address The address which called `approveAndCall` function + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` + * unless throwing + */ + function onApprovalReceived(address owner, uint256 value, bytes memory data) external returns (bytes4); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol new file mode 100644 index 0000000..b97c4da --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC165.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Implementer.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Implementer.sol new file mode 100644 index 0000000..a83a7a3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Implementer.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC1820Implementer.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Registry.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Registry.sol new file mode 100644 index 0000000..1b1ba9f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC1820Registry.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1820Registry.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC1820Registry.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol new file mode 100644 index 0000000..a819316 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol new file mode 100644 index 0000000..aa5c639 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/IERC20Metadata.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC2309.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC2309.sol new file mode 100644 index 0000000..b3fec44 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC2309.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC2309.sol) + +pragma solidity ^0.8.0; + +/** + * @dev ERC-2309: ERC-721 Consecutive Transfer Extension. + * + * _Available since v4.8._ + */ +interface IERC2309 { + /** + * @dev Emitted when the tokens from `fromTokenId` to `toTokenId` are transferred from `fromAddress` to `toAddress`. + */ + event ConsecutiveTransfer( + uint256 indexed fromTokenId, + uint256 toTokenId, + address indexed fromAddress, + address indexed toAddress + ); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC2612.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC2612.sol new file mode 100644 index 0000000..6dfdf6f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC2612.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC2612.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/IERC20Permit.sol"; + +interface IERC2612 is IERC20Permit {} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol new file mode 100644 index 0000000..1c9448a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC2981.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol) + +pragma solidity ^0.8.0; + +import "../utils/introspection/IERC165.sol"; + +/** + * @dev Interface for the NFT Royalty Standard. + * + * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal + * support for royalty payments across all NFT marketplaces and ecosystem participants. + * + * _Available since v4.5._ + */ +interface IERC2981 is IERC165 { + /** + * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of + * exchange. The royalty amount is denominated and should be paid in that same unit of exchange. + */ + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view returns (address receiver, uint256 royaltyAmount); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC3156.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC3156.sol new file mode 100644 index 0000000..1238190 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC3156.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; +import "./IERC3156FlashLender.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol new file mode 100644 index 0000000..c3b4f1e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC3156 FlashBorrower, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashBorrower { + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashLender.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashLender.sol new file mode 100644 index 0000000..3101283 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashLender.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol) + +pragma solidity ^0.8.0; + +import "./IERC3156FlashBorrower.sol"; + +/** + * @dev Interface of the ERC3156 FlashLender, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * _Available since v4.1._ + */ +interface IERC3156FlashLender { + /** + * @dev The amount of currency available to be lended. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan(address token) external view returns (uint256); + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee(address token, uint256 amount) external view returns (uint256); + + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol new file mode 100644 index 0000000..08e5de7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; +import "../token/ERC20/extensions/IERC20Metadata.sol"; + +/** + * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in + * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. + * + * _Available since v4.7._ + */ +interface IERC4626 is IERC20, IERC20Metadata { + event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); + + event Withdraw( + address indexed sender, + address indexed receiver, + address indexed owner, + uint256 assets, + uint256 shares + ); + + /** + * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + * + * - MUST be an ERC-20 token contract. + * - MUST NOT revert. + */ + function asset() external view returns (address assetTokenAddress); + + /** + * @dev Returns the total amount of the underlying asset that is “managed” by Vault. + * + * - SHOULD include any compounding that occurs from yield. + * - MUST be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT revert. + */ + function totalAssets() external view returns (uint256 totalManagedAssets); + + /** + * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToShares(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToAssets(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, + * through a deposit call. + * + * - MUST return a limited value if receiver is subject to some deposit limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. + * - MUST NOT revert. + */ + function maxDeposit(address receiver) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit + * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called + * in the same transaction. + * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the + * deposit would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewDeposit(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * deposit execution, and are accounted for during deposit. + * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function deposit(uint256 assets, address receiver) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. + * - MUST return a limited value if receiver is subject to some mint limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. + * - MUST NOT revert. + */ + function maxMint(address receiver) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call + * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the + * same transaction. + * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint + * would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by minting. + */ + function previewMint(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint + * execution, and are accounted for during mint. + * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function mint(uint256 shares, address receiver) external returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the + * Vault, through a withdraw call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxWithdraw(address owner) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw + * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if + * called + * in the same transaction. + * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though + * the withdrawal would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewWithdraw(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * withdraw execution, and are accounted for during withdraw. + * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, + * through a redeem call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxRedeem(address owner) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call + * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the + * same transaction. + * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the + * redemption would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by redeeming. + */ + function previewRedeem(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * redeem execution, and are accounted for during redeem. + * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC721.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC721.sol new file mode 100644 index 0000000..822b311 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC721.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC721Enumerable.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC721Enumerable.sol new file mode 100644 index 0000000..e39a5a0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC721Enumerable.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/IERC721Enumerable.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC721Metadata.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC721Metadata.sol new file mode 100644 index 0000000..afe2707 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC721Metadata.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/IERC721Metadata.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC721Receiver.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC721Receiver.sol new file mode 100644 index 0000000..c9c153a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC721Receiver.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC777.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC777.sol new file mode 100644 index 0000000..b97ba7b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC777.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC777Recipient.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC777Recipient.sol new file mode 100644 index 0000000..0ce2704 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC777Recipient.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777Recipient.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/IERC777Sender.sol b/lib/openzeppelin-contracts/contracts/interfaces/IERC777Sender.sol new file mode 100644 index 0000000..f1f17a2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/IERC777Sender.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (interfaces/IERC777Sender.sol) + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777Sender.sol"; diff --git a/lib/openzeppelin-contracts/contracts/interfaces/README.adoc b/lib/openzeppelin-contracts/contracts/interfaces/README.adoc new file mode 100644 index 0000000..5b4cedf --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/README.adoc @@ -0,0 +1,56 @@ += Interfaces + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/interfaces + +== List of standardized interfaces +These interfaces are available as `.sol` files, and also as compiler `.json` ABI files (through the npm package). These +are useful to interact with third party contracts that implement them. + +- {IERC20} +- {IERC20Metadata} +- {IERC165} +- {IERC721} +- {IERC721Receiver} +- {IERC721Enumerable} +- {IERC721Metadata} +- {IERC777} +- {IERC777Recipient} +- {IERC777Sender} +- {IERC1155} +- {IERC1155Receiver} +- {IERC1155MetadataURI} +- {IERC1271} +- {IERC1363} +- {IERC1820Implementer} +- {IERC1820Registry} +- {IERC1822Proxiable} +- {IERC2612} +- {IERC2981} +- {IERC3156FlashLender} +- {IERC3156FlashBorrower} +- {IERC4626} + +== Detailed ABI + +{{IERC1271}} + +{{IERC1363}} + +{{IERC1363Receiver}} + +{{IERC1820Implementer}} + +{{IERC1820Registry}} + +{{IERC1822Proxiable}} + +{{IERC2612}} + +{{IERC2981}} + +{{IERC3156FlashLender}} + +{{IERC3156FlashBorrower}} + +{{IERC4626}} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol b/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol new file mode 100644 index 0000000..3b73d74 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) + +pragma solidity ^0.8.0; + +/** + * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified + * proxy whose upgrades are fully controlled by the current implementation. + */ +interface IERC1822Proxiable { + /** + * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation + * address. + * + * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks + * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this + * function revert if invoked through a proxy. + */ + function proxiableUUID() external view returns (bytes32); +} diff --git a/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC2612.sol b/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC2612.sol new file mode 100644 index 0000000..1ea7bf1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC2612.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +// EIP-2612 is Final as of 2022-11-01. This file is deprecated. + +import "./IERC2612.sol"; diff --git a/lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol b/lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol new file mode 100644 index 0000000..8cc14b9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol) + +pragma solidity ^0.8.9; + +import "../utils/Context.sol"; + +/** + * @dev Context variant with ERC2771 support. + */ +abstract contract ERC2771Context is Context { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address private immutable _trustedForwarder; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address trustedForwarder) { + _trustedForwarder = trustedForwarder; + } + + function isTrustedForwarder(address forwarder) public view virtual returns (bool) { + return forwarder == _trustedForwarder; + } + + function _msgSender() internal view virtual override returns (address sender) { + if (isTrustedForwarder(msg.sender)) { + // The assembly code is more direct than the Solidity version using `abi.decode`. + /// @solidity memory-safe-assembly + assembly { + sender := shr(96, calldataload(sub(calldatasize(), 20))) + } + } else { + return super._msgSender(); + } + } + + function _msgData() internal view virtual override returns (bytes calldata) { + if (isTrustedForwarder(msg.sender)) { + return msg.data[:msg.data.length - 20]; + } else { + return super._msgData(); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/metatx/MinimalForwarder.sol b/lib/openzeppelin-contracts/contracts/metatx/MinimalForwarder.sol new file mode 100644 index 0000000..9298ae6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/metatx/MinimalForwarder.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (metatx/MinimalForwarder.sol) + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/EIP712.sol"; + +/** + * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. + * + * MinimalForwarder is mainly meant for testing, as it is missing features to be a good production-ready forwarder. This + * contract does not intend to have all the properties that are needed for a sound forwarding system. A fully + * functioning forwarding system with good properties requires more complexity. We suggest you look at other projects + * such as the GSN which do have the goal of building a system like that. + */ +contract MinimalForwarder is EIP712 { + using ECDSA for bytes32; + + struct ForwardRequest { + address from; + address to; + uint256 value; + uint256 gas; + uint256 nonce; + bytes data; + } + + bytes32 private constant _TYPEHASH = + keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)"); + + mapping(address => uint256) private _nonces; + + constructor() EIP712("MinimalForwarder", "0.0.1") {} + + function getNonce(address from) public view returns (uint256) { + return _nonces[from]; + } + + function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) { + address signer = _hashTypedDataV4( + keccak256(abi.encode(_TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data))) + ).recover(signature); + return _nonces[req.from] == req.nonce && signer == req.from; + } + + function execute( + ForwardRequest calldata req, + bytes calldata signature + ) public payable returns (bool, bytes memory) { + require(verify(req, signature), "MinimalForwarder: signature does not match request"); + _nonces[req.from] = req.nonce + 1; + + (bool success, bytes memory returndata) = req.to.call{gas: req.gas, value: req.value}( + abi.encodePacked(req.data, req.from) + ); + + // Validate that the relayer has sent enough gas for the call. + // See https://ronan.eth.limo/blog/ethereum-gas-dangers/ + if (gasleft() <= req.gas / 63) { + // We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since + // neither revert or assert consume all gas since Solidity 0.8.0 + // https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require + /// @solidity memory-safe-assembly + assembly { + invalid() + } + } + + return (success, returndata); + } +} diff --git a/lib/openzeppelin-contracts/contracts/metatx/README.adoc b/lib/openzeppelin-contracts/contracts/metatx/README.adoc new file mode 100644 index 0000000..eccdeaf --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/metatx/README.adoc @@ -0,0 +1,12 @@ += Meta Transactions + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/metatx + +== Core + +{{ERC2771Context}} + +== Utils + +{{MinimalForwarder}} diff --git a/lib/openzeppelin-contracts/contracts/mocks/AccessControlCrossChainMock.sol b/lib/openzeppelin-contracts/contracts/mocks/AccessControlCrossChainMock.sol new file mode 100644 index 0000000..ffa385c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/AccessControlCrossChainMock.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +import "../access/AccessControlCrossChain.sol"; +import "../crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol"; + +contract AccessControlCrossChainMock is AccessControlCrossChain, CrossChainEnabledArbitrumL2 { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} + + function crossChainRoleAlias(bytes32 role) public pure returns (bytes32) { + return _crossChainRoleAlias(role); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/AccessControlEnumerableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/AccessControlEnumerableMock.sol new file mode 100644 index 0000000..7b15e36 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/AccessControlEnumerableMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/AccessControlEnumerable.sol"; + +contract AccessControlEnumerableMock is AccessControlEnumerable { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/AccessControlMock.sol b/lib/openzeppelin-contracts/contracts/mocks/AccessControlMock.sol new file mode 100644 index 0000000..86f5147 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/AccessControlMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/AccessControl.sol"; + +contract AccessControlMock is AccessControl { + constructor() { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { + _setRoleAdmin(roleId, adminRoleId); + } + + function senderProtected(bytes32 roleId) public onlyRole(roleId) {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/AddressImpl.sol b/lib/openzeppelin-contracts/contracts/mocks/AddressImpl.sol new file mode 100644 index 0000000..b06bec3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/AddressImpl.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Address.sol"; + +contract AddressImpl { + string public sharedAnswer; + + event CallReturnValue(string data); + + function isContract(address account) external view returns (bool) { + return Address.isContract(account); + } + + function sendValue(address payable receiver, uint256 amount) external { + Address.sendValue(receiver, amount); + } + + function functionCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionCallWithValue(address target, bytes calldata data, uint256 value) external payable { + bytes memory returnData = Address.functionCallWithValue(target, data, value); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionStaticCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionStaticCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + function functionDelegateCall(address target, bytes calldata data) external { + bytes memory returnData = Address.functionDelegateCall(target, data); + emit CallReturnValue(abi.decode(returnData, (string))); + } + + // sendValue's tests require the contract to hold Ether + receive() external payable {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ArraysMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ArraysMock.sol new file mode 100644 index 0000000..2ea17a0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ArraysMock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Arrays.sol"; + +contract Uint256ArraysMock { + using Arrays for uint256[]; + + uint256[] private _array; + + constructor(uint256[] memory array) { + _array = array; + } + + function findUpperBound(uint256 element) external view returns (uint256) { + return _array.findUpperBound(element); + } + + function unsafeAccess(uint256 pos) external view returns (uint256) { + return _array.unsafeAccess(pos).value; + } +} + +contract AddressArraysMock { + using Arrays for address[]; + + address[] private _array; + + constructor(address[] memory array) { + _array = array; + } + + function unsafeAccess(uint256 pos) external view returns (address) { + return _array.unsafeAccess(pos).value; + } +} + +contract Bytes32ArraysMock { + using Arrays for bytes32[]; + + bytes32[] private _array; + + constructor(bytes32[] memory array) { + _array = array; + } + + function unsafeAccess(uint256 pos) external view returns (bytes32) { + return _array.unsafeAccess(pos).value; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/BadBeacon.sol b/lib/openzeppelin-contracts/contracts/mocks/BadBeacon.sol new file mode 100644 index 0000000..bedcfed --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/BadBeacon.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract BadBeaconNoImpl {} + +contract BadBeaconNotContract { + function implementation() external pure returns (address) { + return address(0x1); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/Base64Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/Base64Mock.sol new file mode 100644 index 0000000..b255487 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/Base64Mock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Base64.sol"; + +contract Base64Mock { + function encode(bytes memory value) external pure returns (string memory) { + return Base64.encode(value); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/BitmapMock.sol b/lib/openzeppelin-contracts/contracts/mocks/BitmapMock.sol new file mode 100644 index 0000000..ccf8486 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/BitmapMock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/BitMaps.sol"; + +contract BitMapMock { + using BitMaps for BitMaps.BitMap; + + BitMaps.BitMap private _bitmap; + + function get(uint256 index) public view returns (bool) { + return _bitmap.get(index); + } + + function setTo(uint256 index, bool value) public { + _bitmap.setTo(index, value); + } + + function set(uint256 index) public { + _bitmap.set(index); + } + + function unset(uint256 index) public { + _bitmap.unset(index); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/CallReceiverMock.sol b/lib/openzeppelin-contracts/contracts/mocks/CallReceiverMock.sol new file mode 100644 index 0000000..926db68 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/CallReceiverMock.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract CallReceiverMock { + string public sharedAnswer; + + event MockFunctionCalled(); + event MockFunctionCalledWithArgs(uint256 a, uint256 b); + + uint256[] private _array; + + function mockFunction() public payable returns (string memory) { + emit MockFunctionCalled(); + + return "0x1234"; + } + + function mockFunctionWithArgs(uint256 a, uint256 b) public payable returns (string memory) { + emit MockFunctionCalledWithArgs(a, b); + + return "0x1234"; + } + + function mockFunctionNonPayable() public returns (string memory) { + emit MockFunctionCalled(); + + return "0x1234"; + } + + function mockStaticFunction() public pure returns (string memory) { + return "0x1234"; + } + + function mockFunctionRevertsNoReason() public payable { + revert(); + } + + function mockFunctionRevertsReason() public payable { + revert("CallReceiverMock: reverting"); + } + + function mockFunctionThrows() public payable { + assert(false); + } + + function mockFunctionOutOfGas() public payable { + for (uint256 i = 0; ; ++i) { + _array.push(i); + } + } + + function mockFunctionWritesStorage() public returns (string memory) { + sharedAnswer = "42"; + return "0x1234"; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/CheckpointsMock.sol b/lib/openzeppelin-contracts/contracts/mocks/CheckpointsMock.sol new file mode 100644 index 0000000..874a1d1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/CheckpointsMock.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/CheckpointsMock.js. + +pragma solidity ^0.8.0; + +import "../utils/Checkpoints.sol"; + +contract CheckpointsMock { + using Checkpoints for Checkpoints.History; + + Checkpoints.History private _totalCheckpoints; + + function latest() public view returns (uint256) { + return _totalCheckpoints.latest(); + } + + function latestCheckpoint() public view returns (bool, uint256, uint256) { + return _totalCheckpoints.latestCheckpoint(); + } + + function length() public view returns (uint256) { + return _totalCheckpoints.length(); + } + + function push(uint256 value) public returns (uint256, uint256) { + return _totalCheckpoints.push(value); + } + + function getAtBlock(uint256 blockNumber) public view returns (uint256) { + return _totalCheckpoints.getAtBlock(blockNumber); + } + + function getAtProbablyRecentBlock(uint256 blockNumber) public view returns (uint256) { + return _totalCheckpoints.getAtProbablyRecentBlock(blockNumber); + } +} + +contract Checkpoints224Mock { + using Checkpoints for Checkpoints.Trace224; + + Checkpoints.Trace224 private _totalCheckpoints; + + function latest() public view returns (uint224) { + return _totalCheckpoints.latest(); + } + + function latestCheckpoint() public view returns (bool, uint32, uint224) { + return _totalCheckpoints.latestCheckpoint(); + } + + function length() public view returns (uint256) { + return _totalCheckpoints.length(); + } + + function push(uint32 key, uint224 value) public returns (uint224, uint224) { + return _totalCheckpoints.push(key, value); + } + + function lowerLookup(uint32 key) public view returns (uint224) { + return _totalCheckpoints.lowerLookup(key); + } + + function upperLookup(uint32 key) public view returns (uint224) { + return _totalCheckpoints.upperLookup(key); + } +} + +contract Checkpoints160Mock { + using Checkpoints for Checkpoints.Trace160; + + Checkpoints.Trace160 private _totalCheckpoints; + + function latest() public view returns (uint160) { + return _totalCheckpoints.latest(); + } + + function latestCheckpoint() public view returns (bool, uint96, uint160) { + return _totalCheckpoints.latestCheckpoint(); + } + + function length() public view returns (uint256) { + return _totalCheckpoints.length(); + } + + function push(uint96 key, uint160 value) public returns (uint160, uint160) { + return _totalCheckpoints.push(key, value); + } + + function lowerLookup(uint96 key) public view returns (uint160) { + return _totalCheckpoints.lowerLookup(key); + } + + function upperLookup(uint96 key) public view returns (uint160) { + return _totalCheckpoints.upperLookup(key); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ClashingImplementation.sol b/lib/openzeppelin-contracts/contracts/mocks/ClashingImplementation.sol new file mode 100644 index 0000000..80aca0c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ClashingImplementation.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev Implementation contract with an admin() function made to clash with + * @dev TransparentUpgradeableProxy's to test correct functioning of the + * @dev Transparent Proxy feature. + */ +contract ClashingImplementation { + function admin() external pure returns (address) { + return 0x0000000000000000000000000000000011111142; + } + + function delegatedFunction() external pure returns (bool) { + return true; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ClonesMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ClonesMock.sol new file mode 100644 index 0000000..c65d30c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ClonesMock.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/Clones.sol"; +import "../utils/Address.sol"; + +contract ClonesMock { + using Address for address; + using Clones for address; + + event NewInstance(address instance); + + function clone(address implementation, bytes calldata initdata) public payable { + _initAndEmit(implementation.clone(), initdata); + } + + function cloneDeterministic(address implementation, bytes32 salt, bytes calldata initdata) public payable { + _initAndEmit(implementation.cloneDeterministic(salt), initdata); + } + + function predictDeterministicAddress(address implementation, bytes32 salt) public view returns (address predicted) { + return implementation.predictDeterministicAddress(salt); + } + + function _initAndEmit(address instance, bytes memory initdata) private { + if (initdata.length > 0) { + instance.functionCallWithValue(initdata, msg.value); + } + emit NewInstance(instance); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ConditionalEscrowMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ConditionalEscrowMock.sol new file mode 100644 index 0000000..ececf05 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ConditionalEscrowMock.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/escrow/ConditionalEscrow.sol"; + +// mock class using ConditionalEscrow +contract ConditionalEscrowMock is ConditionalEscrow { + mapping(address => bool) private _allowed; + + function setAllowed(address payee, bool allowed) public { + _allowed[payee] = allowed; + } + + function withdrawalAllowed(address payee) public view override returns (bool) { + return _allowed[payee]; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ContextMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ContextMock.sol new file mode 100644 index 0000000..7759f35 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ContextMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +contract ContextMock is Context { + event Sender(address sender); + + function msgSender() public { + emit Sender(_msgSender()); + } + + event Data(bytes data, uint256 integerValue, string stringValue); + + function msgData(uint256 integerValue, string memory stringValue) public { + emit Data(_msgData(), integerValue, stringValue); + } +} + +contract ContextMockCaller { + function callSender(ContextMock context) public { + context.msgSender(); + } + + function callData(ContextMock context, uint256 integerValue, string memory stringValue) public { + context.msgData(integerValue, stringValue); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/CountersImpl.sol b/lib/openzeppelin-contracts/contracts/mocks/CountersImpl.sol new file mode 100644 index 0000000..651b50b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/CountersImpl.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Counters.sol"; + +contract CountersImpl { + using Counters for Counters.Counter; + + Counters.Counter private _counter; + + function current() public view returns (uint256) { + return _counter.current(); + } + + function increment() public { + _counter.increment(); + } + + function decrement() public { + _counter.decrement(); + } + + function reset() public { + _counter.reset(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/Create2Impl.sol b/lib/openzeppelin-contracts/contracts/mocks/Create2Impl.sol new file mode 100644 index 0000000..6b2f4b7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/Create2Impl.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Create2.sol"; +import "../utils/introspection/ERC1820Implementer.sol"; + +contract Create2Impl { + function deploy(uint256 value, bytes32 salt, bytes memory code) public { + Create2.deploy(value, salt, code); + } + + function deployERC1820Implementer(uint256 value, bytes32 salt) public { + Create2.deploy(value, salt, type(ERC1820Implementer).creationCode); + } + + function computeAddress(bytes32 salt, bytes32 codeHash) public view returns (address) { + return Create2.computeAddress(salt, codeHash); + } + + function computeAddressWithDeployer( + bytes32 salt, + bytes32 codeHash, + address deployer + ) public pure returns (address) { + return Create2.computeAddress(salt, codeHash, deployer); + } + + receive() external payable {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/DoubleEndedQueueMock.sol b/lib/openzeppelin-contracts/contracts/mocks/DoubleEndedQueueMock.sol new file mode 100644 index 0000000..a9436b0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/DoubleEndedQueueMock.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/structs/DoubleEndedQueue.sol"; + +// Bytes32Deque +contract Bytes32DequeMock { + using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque; + + event OperationResult(bytes32 value); + + DoubleEndedQueue.Bytes32Deque private _vector; + + function pushBack(bytes32 value) public { + _vector.pushBack(value); + } + + function pushFront(bytes32 value) public { + _vector.pushFront(value); + } + + function popFront() public returns (bytes32) { + bytes32 value = _vector.popFront(); + emit OperationResult(value); + return value; + } + + function popBack() public returns (bytes32) { + bytes32 value = _vector.popBack(); + emit OperationResult(value); + return value; + } + + function front() public view returns (bytes32) { + return _vector.front(); + } + + function back() public view returns (bytes32) { + return _vector.back(); + } + + function at(uint256 i) public view returns (bytes32) { + return _vector.at(i); + } + + function clear() public { + _vector.clear(); + } + + function length() public view returns (uint256) { + return _vector.length(); + } + + function empty() public view returns (bool) { + return _vector.empty(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/DummyImplementation.sol b/lib/openzeppelin-contracts/contracts/mocks/DummyImplementation.sol new file mode 100644 index 0000000..ddcca66 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/DummyImplementation.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +abstract contract Impl { + function version() public pure virtual returns (string memory); +} + +contract DummyImplementation { + uint256 public value; + string public text; + uint256[] public values; + + function initializeNonPayable() public { + value = 10; + } + + function initializePayable() public payable { + value = 100; + } + + function initializeNonPayableWithValue(uint256 _value) public { + value = _value; + } + + function initializePayableWithValue(uint256 _value) public payable { + value = _value; + } + + function initialize(uint256 _value, string memory _text, uint256[] memory _values) public { + value = _value; + text = _text; + values = _values; + } + + function get() public pure returns (bool) { + return true; + } + + function version() public pure virtual returns (string memory) { + return "V1"; + } + + function reverts() public pure { + require(false, "DummyImplementation reverted"); + } +} + +contract DummyImplementationV2 is DummyImplementation { + function migrate(uint256 newVal) public payable { + value = newVal; + } + + function version() public pure override returns (string memory) { + return "V2"; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ECDSAMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ECDSAMock.sol new file mode 100644 index 0000000..cfc37d2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ECDSAMock.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; + +contract ECDSAMock { + using ECDSA for bytes32; + using ECDSA for bytes; + + function recover(bytes32 hash, bytes memory signature) public pure returns (address) { + return hash.recover(signature); + } + + // solhint-disable-next-line func-name-mixedcase + function recover_v_r_s(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure returns (address) { + return hash.recover(v, r, s); + } + + // solhint-disable-next-line func-name-mixedcase + function recover_r_vs(bytes32 hash, bytes32 r, bytes32 vs) public pure returns (address) { + return hash.recover(r, vs); + } + + function toEthSignedMessageHash(bytes32 hash) public pure returns (bytes32) { + return hash.toEthSignedMessageHash(); + } + + function toEthSignedMessageHash(bytes memory s) public pure returns (bytes32) { + return s.toEthSignedMessageHash(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/EIP712External.sol b/lib/openzeppelin-contracts/contracts/mocks/EIP712External.sol new file mode 100644 index 0000000..bc5b126 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/EIP712External.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/ECDSA.sol"; +import "../utils/cryptography/EIP712.sol"; + +contract EIP712External is EIP712 { + constructor(string memory name, string memory version) EIP712(name, version) {} + + function domainSeparator() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + function verify(bytes memory signature, address signer, address mailTo, string memory mailContents) external view { + bytes32 digest = _hashTypedDataV4( + keccak256(abi.encode(keccak256("Mail(address to,string contents)"), mailTo, keccak256(bytes(mailContents)))) + ); + address recoveredSigner = ECDSA.recover(digest, signature); + require(recoveredSigner == signer); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1155BurnableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1155BurnableMock.sol new file mode 100644 index 0000000..3334523 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1155BurnableMock.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/extensions/ERC1155Burnable.sol"; + +contract ERC1155BurnableMock is ERC1155Burnable { + constructor(string memory uri) ERC1155(uri) {} + + function mint(address to, uint256 id, uint256 value, bytes memory data) public { + _mint(to, id, value, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1155Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1155Mock.sol new file mode 100644 index 0000000..6bfc86c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1155Mock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/ERC1155.sol"; + +/** + * @title ERC1155Mock + * This mock just publicizes internal functions for testing purposes + */ +contract ERC1155Mock is ERC1155 { + constructor(string memory uri) ERC1155(uri) {} + + function setURI(string memory newuri) public { + _setURI(newuri); + } + + function mint(address to, uint256 id, uint256 value, bytes memory data) public { + _mint(to, id, value, data); + } + + function mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) public { + _mintBatch(to, ids, values, data); + } + + function burn(address owner, uint256 id, uint256 value) public { + _burn(owner, id, value); + } + + function burnBatch(address owner, uint256[] memory ids, uint256[] memory values) public { + _burnBatch(owner, ids, values); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1155PausableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1155PausableMock.sol new file mode 100644 index 0000000..cd06823 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1155PausableMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155Pausable.sol"; + +contract ERC1155PausableMock is ERC1155Mock, ERC1155Pausable { + constructor(string memory uri) ERC1155Mock(uri) {} + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal override(ERC1155, ERC1155Pausable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1155ReceiverMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1155ReceiverMock.sol new file mode 100644 index 0000000..b2d505c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1155ReceiverMock.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC1155/IERC1155Receiver.sol"; +import "../utils/introspection/ERC165.sol"; + +contract ERC1155ReceiverMock is ERC165, IERC1155Receiver { + bytes4 private _recRetval; + bool private _recReverts; + bytes4 private _batRetval; + bool private _batReverts; + + event Received(address operator, address from, uint256 id, uint256 value, bytes data, uint256 gas); + event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data, uint256 gas); + + constructor(bytes4 recRetval, bool recReverts, bytes4 batRetval, bool batReverts) { + _recRetval = recRetval; + _recReverts = recReverts; + _batRetval = batRetval; + _batReverts = batReverts; + } + + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external override returns (bytes4) { + require(!_recReverts, "ERC1155ReceiverMock: reverting on receive"); + emit Received(operator, from, id, value, data, gasleft()); + return _recRetval; + } + + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external override returns (bytes4) { + require(!_batReverts, "ERC1155ReceiverMock: reverting on batch receive"); + emit BatchReceived(operator, from, ids, values, data, gasleft()); + return _batRetval; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1155SupplyMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1155SupplyMock.sol new file mode 100644 index 0000000..9c0cd7b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1155SupplyMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155Supply.sol"; + +contract ERC1155SupplyMock is ERC1155Mock, ERC1155Supply { + constructor(string memory uri) ERC1155Mock(uri) {} + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal override(ERC1155, ERC1155Supply) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1155URIStorageMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1155URIStorageMock.sol new file mode 100644 index 0000000..ba12b28 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1155URIStorageMock.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC1155Mock.sol"; +import "../token/ERC1155/extensions/ERC1155URIStorage.sol"; + +contract ERC1155URIStorageMock is ERC1155Mock, ERC1155URIStorage { + constructor(string memory _uri) ERC1155Mock(_uri) {} + + function uri(uint256 tokenId) public view override(ERC1155, ERC1155URIStorage) returns (string memory) { + return ERC1155URIStorage.uri(tokenId); + } + + function setURI(uint256 tokenId, string memory _tokenURI) public { + _setURI(tokenId, _tokenURI); + } + + function setBaseURI(string memory baseURI) public { + _setBaseURI(baseURI); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol new file mode 100644 index 0000000..0152889 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable.sol"; +import "../interfaces/IERC1271.sol"; +import "../utils/cryptography/ECDSA.sol"; + +contract ERC1271WalletMock is Ownable, IERC1271 { + constructor(address originalOwner) { + transferOwnership(originalOwner); + } + + function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) { + return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0); + } +} + +contract ERC1271MaliciousMock is IERC1271 { + function isValidSignature(bytes32, bytes memory) public pure override returns (bytes4) { + assembly { + mstore(0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + return(0, 32) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165InterfacesSupported.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165InterfacesSupported.sol new file mode 100644 index 0000000..7a5e5bc --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165InterfacesSupported.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * https://eips.ethereum.org/EIPS/eip-214#specification + * From the specification: + * > Any attempts to make state-changing operations inside an execution instance with STATIC set to true will instead + * throw an exception. + * > These operations include [...], LOG0, LOG1, LOG2, [...] + * + * therefore, because this contract is staticcall'd we need to not emit events (which is how solidity-coverage works) + * solidity-coverage ignores the /mocks folder, so we duplicate its implementation here to avoid instrumenting it + */ +contract SupportsInterfaceWithLookupMock is IERC165 { + /* + * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 + */ + bytes4 public constant INTERFACE_ID_ERC165 = 0x01ffc9a7; + + /** + * @dev A mapping of interface id to whether or not it's supported. + */ + mapping(bytes4 => bool) private _supportedInterfaces; + + /** + * @dev A contract implementing SupportsInterfaceWithLookup + * implement ERC165 itself. + */ + constructor() { + _registerInterface(INTERFACE_ID_ERC165); + } + + /** + * @dev Implement supportsInterface(bytes4) using a lookup table. + */ + function supportsInterface(bytes4 interfaceId) public view override returns (bool) { + return _supportedInterfaces[interfaceId]; + } + + /** + * @dev Private method for registering an interface. + */ + function _registerInterface(bytes4 interfaceId) internal { + require(interfaceId != 0xffffffff, "ERC165InterfacesSupported: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} + +contract ERC165InterfacesSupported is SupportsInterfaceWithLookupMock { + constructor(bytes4[] memory interfaceIds) { + for (uint256 i = 0; i < interfaceIds.length; i++) { + _registerInterface(interfaceIds[i]); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MaliciousData.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MaliciousData.sol new file mode 100644 index 0000000..2446f3d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MaliciousData.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165MaliciousData { + function supportsInterface(bytes4) public pure returns (bool) { + assembly { + mstore(0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + return(0, 32) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MissingData.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MissingData.sol new file mode 100644 index 0000000..59cd51a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MissingData.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165MissingData { + function supportsInterface(bytes4 interfaceId) public view {} // missing return +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165NotSupported.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165NotSupported.sol new file mode 100644 index 0000000..486c7f0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165NotSupported.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ERC165NotSupported {} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165ReturnBomb.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165ReturnBomb.sol new file mode 100644 index 0000000..e53235d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165ReturnBomb.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +contract ERC165ReturnBombMock is IERC165 { + function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { + if (interfaceId == type(IERC165).interfaceId) { + assembly { + mstore(0, 1) + } + } + assembly { + return(0, 101500) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165CheckerMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165CheckerMock.sol new file mode 100644 index 0000000..9ff7e7d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165CheckerMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165Checker.sol"; + +contract ERC165CheckerMock { + using ERC165Checker for address; + + function supportsERC165(address account) public view returns (bool) { + return account.supportsERC165(); + } + + function supportsInterface(address account, bytes4 interfaceId) public view returns (bool) { + return account.supportsInterface(interfaceId); + } + + function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool) { + return account.supportsAllInterfaces(interfaceIds); + } + + function getSupportedInterfaces(address account, bytes4[] memory interfaceIds) public view returns (bool[] memory) { + return account.getSupportedInterfaces(interfaceIds); + } + + function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) public view returns (bool) { + return account.supportsERC165InterfaceUnchecked(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165Mock.sol new file mode 100644 index 0000000..c123d0a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165Mock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165.sol"; + +contract ERC165Mock is ERC165 {} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC165StorageMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC165StorageMock.sol new file mode 100644 index 0000000..4b0bae9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC165StorageMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC165Storage.sol"; + +contract ERC165StorageMock is ERC165Storage { + function registerInterface(bytes4 interfaceId) public { + _registerInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC1820ImplementerMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC1820ImplementerMock.sol new file mode 100644 index 0000000..a6012d7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC1820ImplementerMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/introspection/ERC1820Implementer.sol"; + +contract ERC1820ImplementerMock is ERC1820Implementer { + function registerInterfaceForAddress(bytes32 interfaceHash, address account) public { + _registerInterfaceForAddress(interfaceHash, account); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20BurnableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20BurnableMock.sol new file mode 100644 index 0000000..0ed6c0c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20BurnableMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Burnable.sol"; + +contract ERC20BurnableMock is ERC20Burnable { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20CappedMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20CappedMock.sol new file mode 100644 index 0000000..e69aadf --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20CappedMock.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Capped.sol"; + +contract ERC20CappedMock is ERC20Capped { + constructor(string memory name, string memory symbol, uint256 cap) ERC20(name, symbol) ERC20Capped(cap) {} + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20DecimalsMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20DecimalsMock.sol new file mode 100644 index 0000000..7677e9d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20DecimalsMock.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/ERC20.sol"; + +contract ERC20DecimalsMock is ERC20 { + uint8 private immutable _decimals; + + constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { + _decimals = decimals_; + } + + function decimals() public view override returns (uint8) { + return _decimals; + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20FlashMintMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20FlashMintMock.sol new file mode 100644 index 0000000..ff6f252 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20FlashMintMock.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20FlashMint.sol"; + +contract ERC20FlashMintMock is ERC20FlashMint { + uint256 _flashFeeAmount; + address _flashFeeReceiverAddress; + + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function setFlashFee(uint256 amount) public { + _flashFeeAmount = amount; + } + + function _flashFee(address, uint256) internal view override returns (uint256) { + return _flashFeeAmount; + } + + function setFlashFeeReceiver(address receiver) public { + _flashFeeReceiverAddress = receiver; + } + + function flashFeeReceiver() public view returns (address) { + return _flashFeeReceiver(); + } + + function _flashFeeReceiver() internal view override returns (address) { + return _flashFeeReceiverAddress; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20Mock.sol new file mode 100644 index 0000000..16ffad1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20Mock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/ERC20.sol"; + +// mock class using ERC20 +contract ERC20Mock is ERC20 { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) payable ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function transferInternal(address from, address to, uint256 value) public { + _transfer(from, to, value); + } + + function approveInternal(address owner, address spender, uint256 value) public { + _approve(owner, spender, value); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20PausableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20PausableMock.sol new file mode 100644 index 0000000..19160ba --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20PausableMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Pausable.sol"; + +// mock class using ERC20Pausable +contract ERC20PausableMock is ERC20Pausable { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public { + _burn(from, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20PermitMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20PermitMock.sol new file mode 100644 index 0000000..8daa955 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20PermitMock.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Permit.sol"; + +contract ERC20PermitMock is ERC20Permit { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) payable ERC20(name, symbol) ERC20Permit(name) { + _mint(initialAccount, initialBalance); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20SnapshotMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20SnapshotMock.sol new file mode 100644 index 0000000..cb30483 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20SnapshotMock.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Snapshot.sol"; + +contract ERC20SnapshotMock is ERC20Snapshot { + constructor( + string memory name, + string memory symbol, + address initialAccount, + uint256 initialBalance + ) ERC20(name, symbol) { + _mint(initialAccount, initialBalance); + } + + function snapshot() public { + _snapshot(); + } + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20VotesCompMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20VotesCompMock.sol new file mode 100644 index 0000000..171071f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20VotesCompMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20VotesComp.sol"; + +contract ERC20VotesCompMock is ERC20VotesComp { + constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20VotesMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20VotesMock.sol new file mode 100644 index 0000000..0975e8b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20VotesMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Votes.sol"; + +contract ERC20VotesMock is ERC20Votes { + constructor(string memory name, string memory symbol) ERC20(name, symbol) ERC20Permit(name) {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } + + function burn(address account, uint256 amount) public { + _burn(account, amount); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC20WrapperMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC20WrapperMock.sol new file mode 100644 index 0000000..cf34a7a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC20WrapperMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20Wrapper.sol"; + +contract ERC20WrapperMock is ERC20Wrapper { + constructor( + IERC20 _underlyingToken, + string memory name, + string memory symbol + ) ERC20(name, symbol) ERC20Wrapper(_underlyingToken) {} + + function recover(address account) public returns (uint256) { + return _recover(account); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC2771ContextMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC2771ContextMock.sol new file mode 100644 index 0000000..387df78 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC2771ContextMock.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "./ContextMock.sol"; +import "../metatx/ERC2771Context.sol"; + +// By inheriting from ERC2771Context, Context's internal functions are overridden automatically +contract ERC2771ContextMock is ContextMock, ERC2771Context { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address trustedForwarder) ERC2771Context(trustedForwarder) { + emit Sender(_msgSender()); // _msgSender() should be accessible during construction + } + + function _msgSender() internal view override(Context, ERC2771Context) returns (address) { + return ERC2771Context._msgSender(); + } + + function _msgData() internal view override(Context, ERC2771Context) returns (bytes calldata) { + return ERC2771Context._msgData(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC3156FlashBorrowerMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC3156FlashBorrowerMock.sol new file mode 100644 index 0000000..6a4410f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC3156FlashBorrowerMock.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/IERC20.sol"; +import "../interfaces/IERC3156.sol"; +import "../utils/Address.sol"; + +/** + * @dev WARNING: this IERC3156FlashBorrower mock implementation is for testing purposes ONLY. + * Writing a secure flash lock borrower is not an easy task, and should be done with the utmost care. + * This is not an example of how it should be done, and no pattern present in this mock should be considered secure. + * Following best practices, always have your contract properly audited before using them to manipulate important funds on + * live networks. + */ +contract ERC3156FlashBorrowerMock is IERC3156FlashBorrower { + bytes32 internal constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + bool immutable _enableApprove; + bool immutable _enableReturn; + + event BalanceOf(address token, address account, uint256 value); + event TotalSupply(address token, uint256 value); + + constructor(bool enableReturn, bool enableApprove) { + _enableApprove = enableApprove; + _enableReturn = enableReturn; + } + + function onFlashLoan( + address /*initiator*/, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) public override returns (bytes32) { + require(msg.sender == token); + + emit BalanceOf(token, address(this), IERC20(token).balanceOf(address(this))); + emit TotalSupply(token, IERC20(token).totalSupply()); + + if (data.length > 0) { + // WARNING: This code is for testing purposes only! Do not use. + Address.functionCall(token, data); + } + + if (_enableApprove) { + IERC20(token).approve(token, amount + fee); + } + + return _enableReturn ? _RETURN_VALUE : bytes32(0); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC4626Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC4626Mock.sol new file mode 100644 index 0000000..9c13346 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC4626Mock.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC4626.sol"; + +contract ERC4626Mock is ERC4626 { + constructor(IERC20Metadata asset, string memory name, string memory symbol) ERC20(name, symbol) ERC4626(asset) {} + + function mockMint(address account, uint256 amount) public { + _mint(account, amount); + } + + function mockBurn(address account, uint256 amount) public { + _burn(account, amount); + } +} + +contract ERC4626DecimalMock is ERC4626Mock { + using Math for uint256; + + uint8 private immutable _decimals; + + constructor( + IERC20Metadata asset, + string memory name, + string memory symbol, + uint8 decimalsOverride + ) ERC4626Mock(asset, name, symbol) { + _decimals = decimalsOverride; + } + + function decimals() public view virtual override returns (uint8) { + return _decimals; + } + + function _initialConvertToShares( + uint256 assets, + Math.Rounding rounding + ) internal view virtual override returns (uint256 shares) { + return assets.mulDiv(10 ** decimals(), 10 ** super.decimals(), rounding); + } + + function _initialConvertToAssets( + uint256 shares, + Math.Rounding rounding + ) internal view virtual override returns (uint256 assets) { + return shares.mulDiv(10 ** super.decimals(), 10 ** decimals(), rounding); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721BurnableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721BurnableMock.sol new file mode 100644 index 0000000..ecf4276 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721BurnableMock.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Burnable.sol"; + +contract ERC721BurnableMock is ERC721Burnable { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId, bytes memory _data) public { + _safeMint(to, tokenId, _data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721ConsecutiveEnumerableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721ConsecutiveEnumerableMock.sol new file mode 100644 index 0000000..f4f5c58 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721ConsecutiveEnumerableMock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Consecutive.sol"; +import "../token/ERC721/extensions/ERC721Enumerable.sol"; + +contract ERC721ConsecutiveEnumerableMock is ERC721Consecutive, ERC721Enumerable { + constructor( + string memory name, + string memory symbol, + address[] memory receivers, + uint96[] memory amounts + ) ERC721(name, symbol) { + for (uint256 i = 0; i < receivers.length; ++i) { + _mintConsecutive(receivers[i], amounts[i]); + } + } + + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721, ERC721Enumerable) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function _ownerOf(uint256 tokenId) internal view virtual override(ERC721, ERC721Consecutive) returns (address) { + return super._ownerOf(tokenId); + } + + function _mint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Consecutive) { + super._mint(to, tokenId); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721, ERC721Enumerable) { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + function _afterTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721, ERC721Consecutive) { + super._afterTokenTransfer(from, to, firstTokenId, batchSize); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721ConsecutiveMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721ConsecutiveMock.sol new file mode 100644 index 0000000..add5ab1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721ConsecutiveMock.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Burnable.sol"; +import "../token/ERC721/extensions/ERC721Consecutive.sol"; +import "../token/ERC721/extensions/ERC721Enumerable.sol"; +import "../token/ERC721/extensions/ERC721Pausable.sol"; +import "../token/ERC721/extensions/ERC721Votes.sol"; + +/** + * @title ERC721ConsecutiveMock + */ +contract ERC721ConsecutiveMock is ERC721Burnable, ERC721Consecutive, ERC721Pausable, ERC721Votes { + constructor( + string memory name, + string memory symbol, + address[] memory delegates, + address[] memory receivers, + uint96[] memory amounts + ) ERC721(name, symbol) EIP712(name, "1") { + for (uint256 i = 0; i < delegates.length; ++i) { + _delegate(delegates[i], delegates[i]); + } + + for (uint256 i = 0; i < receivers.length; ++i) { + _mintConsecutive(receivers[i], amounts[i]); + } + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function mintConsecutive(address to, uint96 amount) public { + _mintConsecutive(to, amount); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function _ownerOf(uint256 tokenId) internal view virtual override(ERC721, ERC721Consecutive) returns (address) { + return super._ownerOf(tokenId); + } + + function _mint(address to, uint256 tokenId) internal virtual override(ERC721, ERC721Consecutive) { + super._mint(to, tokenId); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721, ERC721Pausable) { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + function _afterTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721, ERC721Votes, ERC721Consecutive) { + super._afterTokenTransfer(from, to, firstTokenId, batchSize); + } +} + +contract ERC721ConsecutiveNoConstructorMintMock is ERC721Consecutive { + constructor(string memory name, string memory symbol) ERC721(name, symbol) { + _mint(msg.sender, 0); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721EnumerableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721EnumerableMock.sol new file mode 100644 index 0000000..b7ea94e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721EnumerableMock.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Enumerable.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721EnumerableMock is ERC721Enumerable { + string private _baseTokenURI; + + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function _baseURI() internal view override returns (string memory) { + return _baseTokenURI; + } + + function setBaseURI(string calldata newBaseTokenURI) public { + _baseTokenURI = newBaseTokenURI; + } + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId, bytes memory _data) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721Mock.sol new file mode 100644 index 0000000..a3bc839 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721Mock.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/ERC721.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721Mock is ERC721 { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId, bytes memory _data) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721PausableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721PausableMock.sol new file mode 100644 index 0000000..753842e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721PausableMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Pausable.sol"; + +/** + * @title ERC721PausableMock + * This mock just provides a public mint, burn and exists functions for testing purposes + */ +contract ERC721PausableMock is ERC721Pausable { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId, bytes memory _data) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721ReceiverMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721ReceiverMock.sol new file mode 100644 index 0000000..a4923bf --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721ReceiverMock.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/IERC721Receiver.sol"; + +contract ERC721ReceiverMock is IERC721Receiver { + enum Error { + None, + RevertWithMessage, + RevertWithoutMessage, + Panic + } + + bytes4 private immutable _retval; + Error private immutable _error; + + event Received(address operator, address from, uint256 tokenId, bytes data, uint256 gas); + + constructor(bytes4 retval, Error error) { + _retval = retval; + _error = error; + } + + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes memory data + ) public override returns (bytes4) { + if (_error == Error.RevertWithMessage) { + revert("ERC721ReceiverMock: reverting"); + } else if (_error == Error.RevertWithoutMessage) { + revert(); + } else if (_error == Error.Panic) { + uint256 a = uint256(0) / uint256(0); + a; + } + emit Received(operator, from, tokenId, data, gasleft()); + return _retval; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721RoyaltyMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721RoyaltyMock.sol new file mode 100644 index 0000000..6f19d52 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721RoyaltyMock.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Royalty.sol"; + +contract ERC721RoyaltyMock is ERC721Royalty { + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function setTokenRoyalty(uint256 tokenId, address recipient, uint96 fraction) public { + _setTokenRoyalty(tokenId, recipient, fraction); + } + + function setDefaultRoyalty(address recipient, uint96 fraction) public { + _setDefaultRoyalty(recipient, fraction); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } + + function deleteDefaultRoyalty() public { + _deleteDefaultRoyalty(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721URIStorageMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721URIStorageMock.sol new file mode 100644 index 0000000..4bb26b1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721URIStorageMock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721URIStorage.sol"; + +/** + * @title ERC721Mock + * This mock just provides a public safeMint, mint, and burn functions for testing purposes + */ +contract ERC721URIStorageMock is ERC721URIStorage { + string private _baseTokenURI; + + constructor(string memory name, string memory symbol) ERC721(name, symbol) {} + + function _baseURI() internal view override returns (string memory) { + return _baseTokenURI; + } + + function setBaseURI(string calldata newBaseTokenURI) public { + _baseTokenURI = newBaseTokenURI; + } + + function baseURI() public view returns (string memory) { + return _baseURI(); + } + + function setTokenURI(uint256 tokenId, string memory _tokenURI) public { + _setTokenURI(tokenId, _tokenURI); + } + + function exists(uint256 tokenId) public view returns (bool) { + return _exists(tokenId); + } + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId) public { + _safeMint(to, tokenId); + } + + function safeMint(address to, uint256 tokenId, bytes memory _data) public { + _safeMint(to, tokenId, _data); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC721VotesMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC721VotesMock.sol new file mode 100644 index 0000000..acb51eb --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC721VotesMock.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC721/extensions/ERC721Votes.sol"; + +contract ERC721VotesMock is ERC721Votes { + constructor(string memory name, string memory symbol) ERC721(name, symbol) EIP712(name, "1") {} + + function getTotalSupply() public view returns (uint256) { + return _getTotalSupply(); + } + + function mint(address account, uint256 tokenId) public { + _mint(account, tokenId); + } + + function burn(uint256 tokenId) public { + _burn(tokenId); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC777Mock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC777Mock.sol new file mode 100644 index 0000000..59c00b3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC777Mock.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; +import "../token/ERC777/ERC777.sol"; + +contract ERC777Mock is Context, ERC777 { + event BeforeTokenTransfer(); + + constructor( + address initialHolder, + uint256 initialBalance, + string memory name, + string memory symbol, + address[] memory defaultOperators + ) ERC777(name, symbol, defaultOperators) { + _mint(initialHolder, initialBalance, "", ""); + } + + function mintInternal(address to, uint256 amount, bytes memory userData, bytes memory operatorData) public { + _mint(to, amount, userData, operatorData); + } + + function mintInternalExtended( + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) public { + _mint(to, amount, userData, operatorData, requireReceptionAck); + } + + function approveInternal(address holder, address spender, uint256 value) public { + _approve(holder, spender, value); + } + + function _beforeTokenTransfer(address, address, address, uint256) internal override { + emit BeforeTokenTransfer(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ERC777SenderRecipientMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ERC777SenderRecipientMock.sol new file mode 100644 index 0000000..8e8c749 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ERC777SenderRecipientMock.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC777/IERC777.sol"; +import "../token/ERC777/IERC777Sender.sol"; +import "../token/ERC777/IERC777Recipient.sol"; +import "../utils/Context.sol"; +import "../utils/introspection/IERC1820Registry.sol"; +import "../utils/introspection/ERC1820Implementer.sol"; + +contract ERC777SenderRecipientMock is Context, IERC777Sender, IERC777Recipient, ERC1820Implementer { + event TokensToSendCalled( + address operator, + address from, + address to, + uint256 amount, + bytes data, + bytes operatorData, + address token, + uint256 fromBalance, + uint256 toBalance + ); + + event TokensReceivedCalled( + address operator, + address from, + address to, + uint256 amount, + bytes data, + bytes operatorData, + address token, + uint256 fromBalance, + uint256 toBalance + ); + + // Emitted in ERC777Mock. Here for easier decoding + event BeforeTokenTransfer(); + + bool private _shouldRevertSend; + bool private _shouldRevertReceive; + + IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); + bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external override { + if (_shouldRevertSend) { + revert(); + } + + IERC777 token = IERC777(_msgSender()); + + uint256 fromBalance = token.balanceOf(from); + // when called due to burn, to will be the zero address, which will have a balance of 0 + uint256 toBalance = token.balanceOf(to); + + emit TokensToSendCalled( + operator, + from, + to, + amount, + userData, + operatorData, + address(token), + fromBalance, + toBalance + ); + } + + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external override { + if (_shouldRevertReceive) { + revert(); + } + + IERC777 token = IERC777(_msgSender()); + + uint256 fromBalance = token.balanceOf(from); + // when called due to burn, to will be the zero address, which will have a balance of 0 + uint256 toBalance = token.balanceOf(to); + + emit TokensReceivedCalled( + operator, + from, + to, + amount, + userData, + operatorData, + address(token), + fromBalance, + toBalance + ); + } + + function senderFor(address account) public { + _registerInterfaceForAddress(_TOKENS_SENDER_INTERFACE_HASH, account); + + address self = address(this); + if (account == self) { + registerSender(self); + } + } + + function registerSender(address sender) public { + _erc1820.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, sender); + } + + function recipientFor(address account) public { + _registerInterfaceForAddress(_TOKENS_RECIPIENT_INTERFACE_HASH, account); + + address self = address(this); + if (account == self) { + registerRecipient(self); + } + } + + function registerRecipient(address recipient) public { + _erc1820.setInterfaceImplementer(address(this), _TOKENS_RECIPIENT_INTERFACE_HASH, recipient); + } + + function setShouldRevertSend(bool shouldRevert) public { + _shouldRevertSend = shouldRevert; + } + + function setShouldRevertReceive(bool shouldRevert) public { + _shouldRevertReceive = shouldRevert; + } + + function send(IERC777 token, address to, uint256 amount, bytes memory data) public { + // This is 777's send function, not the Solidity send function + token.send(to, amount, data); // solhint-disable-line check-send-result + } + + function burn(IERC777 token, uint256 amount, bytes memory data) public { + token.burn(amount, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/EnumerableMapMock.sol b/lib/openzeppelin-contracts/contracts/mocks/EnumerableMapMock.sol new file mode 100644 index 0000000..b60b1e6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/EnumerableMapMock.sol @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/EnumerableMapMock.js. + +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableMap.sol"; + +// UintToAddressMap +contract UintToAddressMapMock { + using EnumerableMap for EnumerableMap.UintToAddressMap; + + event OperationResult(bool result); + + EnumerableMap.UintToAddressMap private _map; + + function contains(uint256 key) public view returns (bool) { + return _map.contains(key); + } + + function set(uint256 key, address value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(uint256 key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (uint256 key, address value) { + return _map.at(index); + } + + function tryGet(uint256 key) public view returns (bool, address) { + return _map.tryGet(key); + } + + function get(uint256 key) public view returns (address) { + return _map.get(key); + } + + function getWithMessage(uint256 key, string calldata errorMessage) public view returns (address) { + return _map.get(key, errorMessage); + } +} + +// AddressToUintMap +contract AddressToUintMapMock { + using EnumerableMap for EnumerableMap.AddressToUintMap; + + event OperationResult(bool result); + + EnumerableMap.AddressToUintMap private _map; + + function contains(address key) public view returns (bool) { + return _map.contains(key); + } + + function set(address key, uint256 value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(address key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (address key, uint256 value) { + return _map.at(index); + } + + function tryGet(address key) public view returns (bool, uint256) { + return _map.tryGet(key); + } + + function get(address key) public view returns (uint256) { + return _map.get(key); + } + + function getWithMessage(address key, string calldata errorMessage) public view returns (uint256) { + return _map.get(key, errorMessage); + } +} + +// Bytes32ToBytes32Map +contract Bytes32ToBytes32MapMock { + using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map; + + event OperationResult(bool result); + + EnumerableMap.Bytes32ToBytes32Map private _map; + + function contains(bytes32 key) public view returns (bool) { + return _map.contains(key); + } + + function set(bytes32 key, bytes32 value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(bytes32 key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (bytes32 key, bytes32 value) { + return _map.at(index); + } + + function tryGet(bytes32 key) public view returns (bool, bytes32) { + return _map.tryGet(key); + } + + function get(bytes32 key) public view returns (bytes32) { + return _map.get(key); + } + + function getWithMessage(bytes32 key, string calldata errorMessage) public view returns (bytes32) { + return _map.get(key, errorMessage); + } +} + +// UintToUintMap +contract UintToUintMapMock { + using EnumerableMap for EnumerableMap.UintToUintMap; + + event OperationResult(bool result); + + EnumerableMap.UintToUintMap private _map; + + function contains(uint256 key) public view returns (bool) { + return _map.contains(key); + } + + function set(uint256 key, uint256 value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(uint256 key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (uint256 key, uint256 value) { + return _map.at(index); + } + + function tryGet(uint256 key) public view returns (bool, uint256) { + return _map.tryGet(key); + } + + function get(uint256 key) public view returns (uint256) { + return _map.get(key); + } + + function getWithMessage(uint256 key, string calldata errorMessage) public view returns (uint256) { + return _map.get(key, errorMessage); + } +} + +// Bytes32ToUintMap +contract Bytes32ToUintMapMock { + using EnumerableMap for EnumerableMap.Bytes32ToUintMap; + + event OperationResult(bool result); + + EnumerableMap.Bytes32ToUintMap private _map; + + function contains(bytes32 key) public view returns (bool) { + return _map.contains(key); + } + + function set(bytes32 key, uint256 value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(bytes32 key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (bytes32 key, uint256 value) { + return _map.at(index); + } + + function tryGet(bytes32 key) public view returns (bool, uint256) { + return _map.tryGet(key); + } + + function get(bytes32 key) public view returns (uint256) { + return _map.get(key); + } + + function getWithMessage(bytes32 key, string calldata errorMessage) public view returns (uint256) { + return _map.get(key, errorMessage); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/EnumerableSetMock.sol b/lib/openzeppelin-contracts/contracts/mocks/EnumerableSetMock.sol new file mode 100644 index 0000000..f75f38a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/EnumerableSetMock.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/EnumerableSetMock.js. + +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableSet.sol"; + +// Bytes32Set +contract EnumerableBytes32SetMock { + using EnumerableSet for EnumerableSet.Bytes32Set; + + event OperationResult(bool result); + + EnumerableSet.Bytes32Set private _set; + + function contains(bytes32 value) public view returns (bool) { + return _set.contains(value); + } + + function add(bytes32 value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(bytes32 value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (bytes32) { + return _set.at(index); + } + + function values() public view returns (bytes32[] memory) { + return _set.values(); + } +} + +// AddressSet +contract EnumerableAddressSetMock { + using EnumerableSet for EnumerableSet.AddressSet; + + event OperationResult(bool result); + + EnumerableSet.AddressSet private _set; + + function contains(address value) public view returns (bool) { + return _set.contains(value); + } + + function add(address value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(address value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (address) { + return _set.at(index); + } + + function values() public view returns (address[] memory) { + return _set.values(); + } +} + +// UintSet +contract EnumerableUintSetMock { + using EnumerableSet for EnumerableSet.UintSet; + + event OperationResult(bool result); + + EnumerableSet.UintSet private _set; + + function contains(uint256 value) public view returns (bool) { + return _set.contains(value); + } + + function add(uint256 value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(uint256 value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (uint256) { + return _set.at(index); + } + + function values() public view returns (uint256[] memory) { + return _set.values(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/EtherReceiverMock.sol b/lib/openzeppelin-contracts/contracts/mocks/EtherReceiverMock.sol new file mode 100644 index 0000000..a11e646 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/EtherReceiverMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract EtherReceiverMock { + bool private _acceptEther; + + function setAcceptEther(bool acceptEther) public { + _acceptEther = acceptEther; + } + + receive() external payable { + if (!_acceptEther) { + revert(); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorCompMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorCompMock.sol new file mode 100644 index 0000000..c2d8733 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorCompMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesComp.sol"; + +contract GovernorCompMock is GovernorVotesComp, GovernorCountingSimple { + constructor(string memory name_, ERC20VotesComp token_) Governor(name_) GovernorVotesComp(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorCompatibilityBravoMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorCompatibilityBravoMock.sol new file mode 100644 index 0000000..d3b4f70 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorCompatibilityBravoMock.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/compatibility/GovernorCompatibilityBravo.sol"; +import "../governance/extensions/GovernorTimelockCompound.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorVotesComp.sol"; + +contract GovernorCompatibilityBravoMock is + GovernorCompatibilityBravo, + GovernorSettings, + GovernorTimelockCompound, + GovernorVotesComp +{ + constructor( + string memory name_, + ERC20VotesComp token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 proposalThreshold_, + ICompoundTimelock timelock_ + ) + Governor(name_) + GovernorTimelockCompound(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, proposalThreshold_) + GovernorVotesComp(token_) + {} + + function supportsInterface( + bytes4 interfaceId + ) public view override(IERC165, Governor, GovernorTimelockCompound) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function state( + uint256 proposalId + ) public view override(IGovernor, Governor, GovernorTimelockCompound) returns (ProposalState) { + return super.state(proposalId); + } + + function proposalEta( + uint256 proposalId + ) public view override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { + return super.proposalEta(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(IGovernor, Governor, GovernorCompatibilityBravo) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public override(IGovernorTimelock, GovernorTimelockCompound) returns (uint256) { + return super.queue(targets, values, calldatas, salt); + } + + function execute( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public payable override(IGovernor, Governor) returns (uint256) { + return super.execute(targets, values, calldatas, salt); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockCompound) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + /** + * @notice WARNING: this is for mock purposes only. Ability to the _cancel function should be restricted for live + * deployments. + */ + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) internal override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, salt); + } + + function _executor() internal view override(Governor, GovernorTimelockCompound) returns (address) { + return super._executor(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorMock.sol new file mode 100644 index 0000000..bd2de33 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorMock.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorProposalThreshold.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorMock is + GovernorProposalThreshold, + GovernorSettings, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorProposalThreshold) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorPreventLateQuorumMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorPreventLateQuorumMock.sol new file mode 100644 index 0000000..b6b5e76 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorPreventLateQuorumMock.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorPreventLateQuorum.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotes.sol"; + +contract GovernorPreventLateQuorumMock is + GovernorSettings, + GovernorVotes, + GovernorCountingSimple, + GovernorPreventLateQuorum +{ + uint256 private _quorum; + + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + uint256 quorum_, + uint64 voteExtension_ + ) + Governor(name_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorPreventLateQuorum(voteExtension_) + { + _quorum = quorum_; + } + + function quorum(uint256) public view override returns (uint256) { + return _quorum; + } + + function proposalDeadline( + uint256 proposalId + ) public view override(Governor, GovernorPreventLateQuorum) returns (uint256) { + return super.proposalDeadline(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _castVote( + uint256 proposalId, + address account, + uint8 support, + string memory reason, + bytes memory params + ) internal override(Governor, GovernorPreventLateQuorum) returns (uint256) { + return super._castVote(proposalId, account, support, reason, params); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorTimelockCompoundMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorTimelockCompoundMock.sol new file mode 100644 index 0000000..75a2f3c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorTimelockCompoundMock.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorTimelockCompound.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorTimelockCompoundMock is + GovernorSettings, + GovernorTimelockCompound, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + ICompoundTimelock timelock_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorTimelockCompound(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockCompound) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + return super.quorum(blockNumber); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } + + /** + * Overriding nightmare + */ + function state( + uint256 proposalId + ) public view override(Governor, GovernorTimelockCompound) returns (ProposalState) { + return super.state(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockCompound) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) internal override(Governor, GovernorTimelockCompound) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, salt); + } + + function _executor() internal view override(Governor, GovernorTimelockCompound) returns (address) { + return super._executor(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorTimelockControlMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorTimelockControlMock.sol new file mode 100644 index 0000000..671a1e0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorTimelockControlMock.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorTimelockControl.sol"; +import "../governance/extensions/GovernorSettings.sol"; +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotesQuorumFraction.sol"; + +contract GovernorTimelockControlMock is + GovernorSettings, + GovernorTimelockControl, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + string memory name_, + IVotes token_, + uint256 votingDelay_, + uint256 votingPeriod_, + TimelockController timelock_, + uint256 quorumNumerator_ + ) + Governor(name_) + GovernorTimelockControl(timelock_) + GovernorSettings(votingDelay_, votingPeriod_, 0) + GovernorVotes(token_) + GovernorVotesQuorumFraction(quorumNumerator_) + {} + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + return super.quorum(blockNumber); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, descriptionHash); + } + + /** + * Overriding nightmare + */ + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256 proposalId) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function nonGovernanceFunction() external {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorVoteMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorVoteMock.sol new file mode 100644 index 0000000..60a3d41 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorVoteMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotes.sol"; + +contract GovernorVoteMocks is GovernorVotes, GovernorCountingSimple { + constructor(string memory name_, IVotes token_) Governor(name_) GovernorVotes(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/GovernorWithParamsMock.sol b/lib/openzeppelin-contracts/contracts/mocks/GovernorWithParamsMock.sol new file mode 100644 index 0000000..b5da890 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/GovernorWithParamsMock.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/extensions/GovernorCountingSimple.sol"; +import "../governance/extensions/GovernorVotes.sol"; + +contract GovernorWithParamsMock is GovernorVotes, GovernorCountingSimple { + event CountParams(uint256 uintParam, string strParam); + + constructor(string memory name_, IVotes token_) Governor(name_) GovernorVotes(token_) {} + + function quorum(uint256) public pure override returns (uint256) { + return 0; + } + + function votingDelay() public pure override returns (uint256) { + return 4; + } + + function votingPeriod() public pure override returns (uint256) { + return 16; + } + + function _getVotes( + address account, + uint256 blockNumber, + bytes memory params + ) internal view override(Governor, GovernorVotes) returns (uint256) { + uint256 reduction = 0; + // If the user provides parameters, we reduce the voting weight by the amount of the integer param + if (params.length > 0) { + (reduction, ) = abi.decode(params, (uint256, string)); + } + // reverts on overflow + return super._getVotes(account, blockNumber, params) - reduction; + } + + function _countVote( + uint256 proposalId, + address account, + uint8 support, + uint256 weight, + bytes memory params + ) internal override(Governor, GovernorCountingSimple) { + if (params.length > 0) { + (uint256 _uintParam, string memory _strParam) = abi.decode(params, (uint256, string)); + emit CountParams(_uintParam, _strParam); + } + return super._countVote(proposalId, account, support, weight, params); + } + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 salt + ) public returns (uint256 proposalId) { + return _cancel(targets, values, calldatas, salt); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/InitializableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/InitializableMock.sol new file mode 100644 index 0000000..34040b6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/InitializableMock.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +/** + * @title InitializableMock + * @dev This contract is a mock to test initializable functionality + */ +contract InitializableMock is Initializable { + bool public initializerRan; + bool public onlyInitializingRan; + uint256 public x; + + function isInitializing() public view returns (bool) { + return _isInitializing(); + } + + function initialize() public initializer { + initializerRan = true; + } + + function initializeOnlyInitializing() public onlyInitializing { + onlyInitializingRan = true; + } + + function initializerNested() public initializer { + initialize(); + } + + function onlyInitializingNested() public initializer { + initializeOnlyInitializing(); + } + + function initializeWithX(uint256 _x) public payable initializer { + x = _x; + } + + function nonInitializable(uint256 _x) public payable { + x = _x; + } + + function fail() public pure { + require(false, "InitializableMock forced failure"); + } +} + +contract ConstructorInitializableMock is Initializable { + bool public initializerRan; + bool public onlyInitializingRan; + + constructor() initializer { + initialize(); + initializeOnlyInitializing(); + } + + function initialize() public initializer { + initializerRan = true; + } + + function initializeOnlyInitializing() public onlyInitializing { + onlyInitializingRan = true; + } +} + +contract ChildConstructorInitializableMock is ConstructorInitializableMock { + bool public childInitializerRan; + + constructor() initializer { + childInitialize(); + } + + function childInitialize() public initializer { + childInitializerRan = true; + } +} + +contract ReinitializerMock is Initializable { + uint256 public counter; + + function getInitializedVersion() public view returns (uint8) { + return _getInitializedVersion(); + } + + function initialize() public initializer { + doStuff(); + } + + function reinitialize(uint8 i) public reinitializer(i) { + doStuff(); + } + + function nestedReinitialize(uint8 i, uint8 j) public reinitializer(i) { + reinitialize(j); + } + + function chainReinitialize(uint8 i, uint8 j) public { + reinitialize(i); + reinitialize(j); + } + + function disableInitializers() public { + _disableInitializers(); + } + + function doStuff() public onlyInitializing { + counter++; + } +} + +contract DisableNew is Initializable { + constructor() { + _disableInitializers(); + } +} + +contract DisableOld is Initializable { + constructor() initializer {} +} + +contract DisableBad1 is DisableNew, DisableOld {} + +contract DisableBad2 is Initializable { + constructor() initializer { + _disableInitializers(); + } +} + +contract DisableOk is DisableOld, DisableNew {} diff --git a/lib/openzeppelin-contracts/contracts/mocks/MathMock.sol b/lib/openzeppelin-contracts/contracts/mocks/MathMock.sol new file mode 100644 index 0000000..be935f9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/MathMock.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/Math.sol"; + +contract MathMock { + function max(uint256 a, uint256 b) public pure returns (uint256) { + return Math.max(a, b); + } + + function min(uint256 a, uint256 b) public pure returns (uint256) { + return Math.min(a, b); + } + + function average(uint256 a, uint256 b) public pure returns (uint256) { + return Math.average(a, b); + } + + function ceilDiv(uint256 a, uint256 b) public pure returns (uint256) { + return Math.ceilDiv(a, b); + } + + function mulDiv(uint256 a, uint256 b, uint256 denominator, Math.Rounding direction) public pure returns (uint256) { + return Math.mulDiv(a, b, denominator, direction); + } + + function sqrt(uint256 a, Math.Rounding direction) public pure returns (uint256) { + return Math.sqrt(a, direction); + } + + function log2(uint256 a, Math.Rounding direction) public pure returns (uint256) { + return Math.log2(a, direction); + } + + function log10(uint256 a, Math.Rounding direction) public pure returns (uint256) { + return Math.log10(a, direction); + } + + function log256(uint256 a, Math.Rounding direction) public pure returns (uint256) { + return Math.log256(a, direction); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/MerkleProofWrapper.sol b/lib/openzeppelin-contracts/contracts/mocks/MerkleProofWrapper.sol new file mode 100644 index 0000000..60741e4 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/MerkleProofWrapper.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/MerkleProof.sol"; + +contract MerkleProofWrapper { + function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) public pure returns (bool) { + return MerkleProof.verify(proof, root, leaf); + } + + function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) public pure returns (bool) { + return MerkleProof.verifyCalldata(proof, root, leaf); + } + + function processProof(bytes32[] memory proof, bytes32 leaf) public pure returns (bytes32) { + return MerkleProof.processProof(proof, leaf); + } + + function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) public pure returns (bytes32) { + return MerkleProof.processProofCalldata(proof, leaf); + } + + function multiProofVerify( + bytes32[] memory proofs, + bool[] memory proofFlag, + bytes32 root, + bytes32[] memory leaves + ) public pure returns (bool) { + return MerkleProof.multiProofVerify(proofs, proofFlag, root, leaves); + } + + function multiProofVerifyCalldata( + bytes32[] calldata proofs, + bool[] calldata proofFlag, + bytes32 root, + bytes32[] memory leaves + ) public pure returns (bool) { + return MerkleProof.multiProofVerifyCalldata(proofs, proofFlag, root, leaves); + } + + function processMultiProof( + bytes32[] memory proofs, + bool[] memory proofFlag, + bytes32[] memory leaves + ) public pure returns (bytes32) { + return MerkleProof.processMultiProof(proofs, proofFlag, leaves); + } + + function processMultiProofCalldata( + bytes32[] calldata proofs, + bool[] calldata proofFlag, + bytes32[] memory leaves + ) public pure returns (bytes32) { + return MerkleProof.processMultiProofCalldata(proofs, proofFlag, leaves); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/MulticallTest.sol b/lib/openzeppelin-contracts/contracts/mocks/MulticallTest.sol new file mode 100644 index 0000000..4e527ef --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/MulticallTest.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./MulticallTokenMock.sol"; + +contract MulticallTest { + function checkReturnValues( + MulticallTokenMock multicallToken, + address[] calldata recipients, + uint256[] calldata amounts + ) external { + bytes[] memory calls = new bytes[](recipients.length); + for (uint256 i = 0; i < recipients.length; i++) { + calls[i] = abi.encodeWithSignature("transfer(address,uint256)", recipients[i], amounts[i]); + } + + bytes[] memory results = multicallToken.multicall(calls); + for (uint256 i = 0; i < results.length; i++) { + require(abi.decode(results[i], (bool))); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/MulticallTokenMock.sol b/lib/openzeppelin-contracts/contracts/mocks/MulticallTokenMock.sol new file mode 100644 index 0000000..de37968 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/MulticallTokenMock.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Multicall.sol"; +import "./ERC20Mock.sol"; + +contract MulticallTokenMock is ERC20Mock, Multicall { + constructor(uint256 initialBalance) ERC20Mock("MulticallToken", "BCT", msg.sender, initialBalance) {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/MultipleInheritanceInitializableMocks.sol b/lib/openzeppelin-contracts/contracts/mocks/MultipleInheritanceInitializableMocks.sol new file mode 100644 index 0000000..b8cf9d9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/MultipleInheritanceInitializableMocks.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +// Sample contracts showing upgradeability with multiple inheritance. +// Child contract inherits from Father and Mother contracts, and Father extends from Gramps. +// +// Human +// / \ +// | Gramps +// | | +// Mother Father +// | | +// -- Child -- + +/** + * Sample base intializable contract that is a human + */ +contract SampleHuman is Initializable { + bool public isHuman; + + function initialize() public initializer { + __SampleHuman_init(); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleHuman_init() internal onlyInitializing { + __SampleHuman_init_unchained(); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleHuman_init_unchained() internal onlyInitializing { + isHuman = true; + } +} + +/** + * Sample base intializable contract that defines a field mother + */ +contract SampleMother is Initializable, SampleHuman { + uint256 public mother; + + function initialize(uint256 value) public initializer { + __SampleMother_init(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleMother_init(uint256 value) internal onlyInitializing { + __SampleHuman_init(); + __SampleMother_init_unchained(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleMother_init_unchained(uint256 value) internal onlyInitializing { + mother = value; + } +} + +/** + * Sample base intializable contract that defines a field gramps + */ +contract SampleGramps is Initializable, SampleHuman { + string public gramps; + + function initialize(string memory value) public initializer { + __SampleGramps_init(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleGramps_init(string memory value) internal onlyInitializing { + __SampleHuman_init(); + __SampleGramps_init_unchained(value); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleGramps_init_unchained(string memory value) internal onlyInitializing { + gramps = value; + } +} + +/** + * Sample base intializable contract that defines a field father and extends from gramps + */ +contract SampleFather is Initializable, SampleGramps { + uint256 public father; + + function initialize(string memory _gramps, uint256 _father) public initializer { + __SampleFather_init(_gramps, _father); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleFather_init(string memory _gramps, uint256 _father) internal onlyInitializing { + __SampleGramps_init(_gramps); + __SampleFather_init_unchained(_father); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleFather_init_unchained(uint256 _father) internal onlyInitializing { + father = _father; + } +} + +/** + * Child extends from mother, father (gramps) + */ +contract SampleChild is Initializable, SampleMother, SampleFather { + uint256 public child; + + function initialize(uint256 _mother, string memory _gramps, uint256 _father, uint256 _child) public initializer { + __SampleChild_init(_mother, _gramps, _father, _child); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleChild_init( + uint256 _mother, + string memory _gramps, + uint256 _father, + uint256 _child + ) internal onlyInitializing { + __SampleMother_init(_mother); + __SampleFather_init(_gramps, _father); + __SampleChild_init_unchained(_child); + } + + // solhint-disable-next-line func-name-mixedcase + function __SampleChild_init_unchained(uint256 _child) internal onlyInitializing { + child = _child; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/Ownable2StepMock.sol b/lib/openzeppelin-contracts/contracts/mocks/Ownable2StepMock.sol new file mode 100644 index 0000000..606d0c9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/Ownable2StepMock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable2Step.sol"; + +contract Ownable2StepMock is Ownable2Step {} diff --git a/lib/openzeppelin-contracts/contracts/mocks/OwnableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/OwnableMock.sol new file mode 100644 index 0000000..d60f1c4 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/OwnableMock.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../access/Ownable.sol"; + +contract OwnableMock is Ownable {} diff --git a/lib/openzeppelin-contracts/contracts/mocks/PausableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/PausableMock.sol new file mode 100644 index 0000000..98bcfd5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/PausableMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/Pausable.sol"; + +contract PausableMock is Pausable { + bool public drasticMeasureTaken; + uint256 public count; + + constructor() { + drasticMeasureTaken = false; + count = 0; + } + + function normalProcess() external whenNotPaused { + count++; + } + + function drasticMeasure() external whenPaused { + drasticMeasureTaken = true; + } + + function pause() external { + _pause(); + } + + function unpause() external { + _unpause(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/PullPaymentMock.sol b/lib/openzeppelin-contracts/contracts/mocks/PullPaymentMock.sol new file mode 100644 index 0000000..8a708e3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/PullPaymentMock.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/PullPayment.sol"; + +// mock class using PullPayment +contract PullPaymentMock is PullPayment { + constructor() payable {} + + // test helper function to call asyncTransfer + function callTransfer(address dest, uint256 amount) public { + _asyncTransfer(dest, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ReentrancyAttack.sol b/lib/openzeppelin-contracts/contracts/mocks/ReentrancyAttack.sol new file mode 100644 index 0000000..4de1812 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ReentrancyAttack.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +contract ReentrancyAttack is Context { + function callSender(bytes4 data) public { + (bool success, ) = _msgSender().call(abi.encodeWithSelector(data)); + require(success, "ReentrancyAttack: failed call"); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/ReentrancyMock.sol b/lib/openzeppelin-contracts/contracts/mocks/ReentrancyMock.sol new file mode 100644 index 0000000..161e1d3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/ReentrancyMock.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../security/ReentrancyGuard.sol"; +import "./ReentrancyAttack.sol"; + +contract ReentrancyMock is ReentrancyGuard { + uint256 public counter; + + constructor() { + counter = 0; + } + + function callback() external nonReentrant { + _count(); + } + + function countLocalRecursive(uint256 n) public nonReentrant { + if (n > 0) { + _count(); + countLocalRecursive(n - 1); + } + } + + function countThisRecursive(uint256 n) public nonReentrant { + if (n > 0) { + _count(); + (bool success, ) = address(this).call(abi.encodeWithSignature("countThisRecursive(uint256)", n - 1)); + require(success, "ReentrancyMock: failed call"); + } + } + + function countAndCall(ReentrancyAttack attacker) public nonReentrant { + _count(); + bytes4 func = bytes4(keccak256("callback()")); + attacker.callSender(func); + } + + function _count() private { + counter += 1; + } + + function guardedCheckEntered() public nonReentrant { + require(_reentrancyGuardEntered()); + } + + function unguardedCheckNotEntered() public view { + require(!_reentrancyGuardEntered()); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/RegressionImplementation.sol b/lib/openzeppelin-contracts/contracts/mocks/RegressionImplementation.sol new file mode 100644 index 0000000..be6b501 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/RegressionImplementation.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +contract Implementation1 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } +} + +contract Implementation2 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } +} + +contract Implementation3 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue(uint256 _number) public view returns (uint256) { + return _value + _number; + } +} + +contract Implementation4 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } + + fallback() external { + _value = 1; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SafeCastMock.sol b/lib/openzeppelin-contracts/contracts/mocks/SafeCastMock.sol new file mode 100644 index 0000000..12a0de6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SafeCastMock.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT +// This file was procedurally generated from scripts/generate/templates/SafeCastMock.js. + +pragma solidity ^0.8.0; + +import "../utils/math/SafeCast.sol"; + +contract SafeCastMock { + using SafeCast for uint256; + using SafeCast for int256; + + function toUint256(int256 a) public pure returns (uint256) { + return a.toUint256(); + } + + function toUint248(uint256 a) public pure returns (uint248) { + return a.toUint248(); + } + + function toUint240(uint256 a) public pure returns (uint240) { + return a.toUint240(); + } + + function toUint232(uint256 a) public pure returns (uint232) { + return a.toUint232(); + } + + function toUint224(uint256 a) public pure returns (uint224) { + return a.toUint224(); + } + + function toUint216(uint256 a) public pure returns (uint216) { + return a.toUint216(); + } + + function toUint208(uint256 a) public pure returns (uint208) { + return a.toUint208(); + } + + function toUint200(uint256 a) public pure returns (uint200) { + return a.toUint200(); + } + + function toUint192(uint256 a) public pure returns (uint192) { + return a.toUint192(); + } + + function toUint184(uint256 a) public pure returns (uint184) { + return a.toUint184(); + } + + function toUint176(uint256 a) public pure returns (uint176) { + return a.toUint176(); + } + + function toUint168(uint256 a) public pure returns (uint168) { + return a.toUint168(); + } + + function toUint160(uint256 a) public pure returns (uint160) { + return a.toUint160(); + } + + function toUint152(uint256 a) public pure returns (uint152) { + return a.toUint152(); + } + + function toUint144(uint256 a) public pure returns (uint144) { + return a.toUint144(); + } + + function toUint136(uint256 a) public pure returns (uint136) { + return a.toUint136(); + } + + function toUint128(uint256 a) public pure returns (uint128) { + return a.toUint128(); + } + + function toUint120(uint256 a) public pure returns (uint120) { + return a.toUint120(); + } + + function toUint112(uint256 a) public pure returns (uint112) { + return a.toUint112(); + } + + function toUint104(uint256 a) public pure returns (uint104) { + return a.toUint104(); + } + + function toUint96(uint256 a) public pure returns (uint96) { + return a.toUint96(); + } + + function toUint88(uint256 a) public pure returns (uint88) { + return a.toUint88(); + } + + function toUint80(uint256 a) public pure returns (uint80) { + return a.toUint80(); + } + + function toUint72(uint256 a) public pure returns (uint72) { + return a.toUint72(); + } + + function toUint64(uint256 a) public pure returns (uint64) { + return a.toUint64(); + } + + function toUint56(uint256 a) public pure returns (uint56) { + return a.toUint56(); + } + + function toUint48(uint256 a) public pure returns (uint48) { + return a.toUint48(); + } + + function toUint40(uint256 a) public pure returns (uint40) { + return a.toUint40(); + } + + function toUint32(uint256 a) public pure returns (uint32) { + return a.toUint32(); + } + + function toUint24(uint256 a) public pure returns (uint24) { + return a.toUint24(); + } + + function toUint16(uint256 a) public pure returns (uint16) { + return a.toUint16(); + } + + function toUint8(uint256 a) public pure returns (uint8) { + return a.toUint8(); + } + + function toInt256(uint256 a) public pure returns (int256) { + return a.toInt256(); + } + + function toInt248(int256 a) public pure returns (int248) { + return a.toInt248(); + } + + function toInt240(int256 a) public pure returns (int240) { + return a.toInt240(); + } + + function toInt232(int256 a) public pure returns (int232) { + return a.toInt232(); + } + + function toInt224(int256 a) public pure returns (int224) { + return a.toInt224(); + } + + function toInt216(int256 a) public pure returns (int216) { + return a.toInt216(); + } + + function toInt208(int256 a) public pure returns (int208) { + return a.toInt208(); + } + + function toInt200(int256 a) public pure returns (int200) { + return a.toInt200(); + } + + function toInt192(int256 a) public pure returns (int192) { + return a.toInt192(); + } + + function toInt184(int256 a) public pure returns (int184) { + return a.toInt184(); + } + + function toInt176(int256 a) public pure returns (int176) { + return a.toInt176(); + } + + function toInt168(int256 a) public pure returns (int168) { + return a.toInt168(); + } + + function toInt160(int256 a) public pure returns (int160) { + return a.toInt160(); + } + + function toInt152(int256 a) public pure returns (int152) { + return a.toInt152(); + } + + function toInt144(int256 a) public pure returns (int144) { + return a.toInt144(); + } + + function toInt136(int256 a) public pure returns (int136) { + return a.toInt136(); + } + + function toInt128(int256 a) public pure returns (int128) { + return a.toInt128(); + } + + function toInt120(int256 a) public pure returns (int120) { + return a.toInt120(); + } + + function toInt112(int256 a) public pure returns (int112) { + return a.toInt112(); + } + + function toInt104(int256 a) public pure returns (int104) { + return a.toInt104(); + } + + function toInt96(int256 a) public pure returns (int96) { + return a.toInt96(); + } + + function toInt88(int256 a) public pure returns (int88) { + return a.toInt88(); + } + + function toInt80(int256 a) public pure returns (int80) { + return a.toInt80(); + } + + function toInt72(int256 a) public pure returns (int72) { + return a.toInt72(); + } + + function toInt64(int256 a) public pure returns (int64) { + return a.toInt64(); + } + + function toInt56(int256 a) public pure returns (int56) { + return a.toInt56(); + } + + function toInt48(int256 a) public pure returns (int48) { + return a.toInt48(); + } + + function toInt40(int256 a) public pure returns (int40) { + return a.toInt40(); + } + + function toInt32(int256 a) public pure returns (int32) { + return a.toInt32(); + } + + function toInt24(int256 a) public pure returns (int24) { + return a.toInt24(); + } + + function toInt16(int256 a) public pure returns (int16) { + return a.toInt16(); + } + + function toInt8(int256 a) public pure returns (int8) { + return a.toInt8(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SafeERC20Helper.sol b/lib/openzeppelin-contracts/contracts/mocks/SafeERC20Helper.sol new file mode 100644 index 0000000..237e320 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SafeERC20Helper.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; +import "../token/ERC20/IERC20.sol"; +import "../token/ERC20/extensions/ERC20Permit.sol"; +import "../token/ERC20/utils/SafeERC20.sol"; + +contract ERC20ReturnFalseMock is Context { + uint256 private _allowance; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function transferFrom(address, address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function approve(address, uint256) public returns (bool) { + _dummy = 0; + return false; + } + + function allowance(address, address) public view returns (uint256) { + require(_dummy == 0); // Dummy read from a state variable so that the function is view + return 0; + } +} + +contract ERC20ReturnTrueMock is Context { + mapping(address => uint256) private _allowances; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function transferFrom(address, address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function approve(address, uint256) public returns (bool) { + _dummy = 0; + return true; + } + + function setAllowance(uint256 allowance_) public { + _allowances[_msgSender()] = allowance_; + } + + function allowance(address owner, address) public view returns (uint256) { + return _allowances[owner]; + } +} + +contract ERC20NoReturnMock is Context { + mapping(address => uint256) private _allowances; + + // IERC20's functions are not pure, but these mock implementations are: to prevent Solidity from issuing warnings, + // we write to a dummy state variable. + uint256 private _dummy; + + function transfer(address, uint256) public { + _dummy = 0; + } + + function transferFrom(address, address, uint256) public { + _dummy = 0; + } + + function approve(address, uint256) public { + _dummy = 0; + } + + function setAllowance(uint256 allowance_) public { + _allowances[_msgSender()] = allowance_; + } + + function allowance(address owner, address) public view returns (uint256) { + return _allowances[owner]; + } +} + +contract ERC20PermitNoRevertMock is + ERC20("ERC20PermitNoRevertMock", "ERC20PermitNoRevertMock"), + ERC20Permit("ERC20PermitNoRevertMock") +{ + function getChainId() external view returns (uint256) { + return block.chainid; + } + + function permitThatMayRevert( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public { + super.permit(owner, spender, value, deadline, v, r, s); + } + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public override { + try this.permitThatMayRevert(owner, spender, value, deadline, v, r, s) { + // do nothing + } catch { + // do nothing + } + } +} + +contract SafeERC20Wrapper is Context { + using SafeERC20 for IERC20; + + IERC20 private _token; + + constructor(IERC20 token) { + _token = token; + } + + function transfer() public { + _token.safeTransfer(address(0), 0); + } + + function transferFrom() public { + _token.safeTransferFrom(address(0), address(0), 0); + } + + function approve(uint256 amount) public { + _token.safeApprove(address(0), amount); + } + + function increaseAllowance(uint256 amount) public { + _token.safeIncreaseAllowance(address(0), amount); + } + + function decreaseAllowance(uint256 amount) public { + _token.safeDecreaseAllowance(address(0), amount); + } + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public { + SafeERC20.safePermit(IERC20Permit(address(_token)), owner, spender, value, deadline, v, r, s); + } + + function setAllowance(uint256 allowance_) public { + ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); + } + + function allowance() public view returns (uint256) { + return _token.allowance(address(0), address(0)); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SafeMathMock.sol b/lib/openzeppelin-contracts/contracts/mocks/SafeMathMock.sol new file mode 100644 index 0000000..ef504e3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SafeMathMock.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SafeMath.sol"; + +contract SafeMathMock { + function tryAdd(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryAdd(a, b); + } + + function trySub(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.trySub(a, b); + } + + function tryMul(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryMul(a, b); + } + + function tryDiv(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryDiv(a, b); + } + + function tryMod(uint256 a, uint256 b) public pure returns (bool flag, uint256 value) { + return SafeMath.tryMod(a, b); + } + + // using the do* naming convention to avoid warnings due to clashing opcode names + + function doAdd(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.add(a, b); + } + + function doSub(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.sub(a, b); + } + + function doMul(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.mul(a, b); + } + + function doDiv(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.div(a, b); + } + + function doMod(uint256 a, uint256 b) public pure returns (uint256) { + return SafeMath.mod(a, b); + } + + function subWithMessage(uint256 a, uint256 b, string memory errorMessage) public pure returns (uint256) { + return SafeMath.sub(a, b, errorMessage); + } + + function divWithMessage(uint256 a, uint256 b, string memory errorMessage) public pure returns (uint256) { + return SafeMath.div(a, b, errorMessage); + } + + function modWithMessage(uint256 a, uint256 b, string memory errorMessage) public pure returns (uint256) { + return SafeMath.mod(a, b, errorMessage); + } + + function addMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.add(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function subMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.sub(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function mulMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.mul(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function divMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.div(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } + + function modMemoryCheck() public pure returns (uint256 mem) { + uint256 length = 32; + assembly { + mem := mload(0x40) + } + for (uint256 i = 0; i < length; ++i) { + SafeMath.mod(1, 1); + } + assembly { + mem := sub(mload(0x40), mem) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SignatureCheckerMock.sol b/lib/openzeppelin-contracts/contracts/mocks/SignatureCheckerMock.sol new file mode 100644 index 0000000..5671540 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SignatureCheckerMock.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/cryptography/SignatureChecker.sol"; + +contract SignatureCheckerMock { + using SignatureChecker for address; + + function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) public view returns (bool) { + return signer.isValidSignatureNow(hash, signature); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SignedMathMock.sol b/lib/openzeppelin-contracts/contracts/mocks/SignedMathMock.sol new file mode 100644 index 0000000..5a0b270 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SignedMathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SignedMath.sol"; + +contract SignedMathMock { + function max(int256 a, int256 b) public pure returns (int256) { + return SignedMath.max(a, b); + } + + function min(int256 a, int256 b) public pure returns (int256) { + return SignedMath.min(a, b); + } + + function average(int256 a, int256 b) public pure returns (int256) { + return SignedMath.average(a, b); + } + + function abs(int256 n) public pure returns (uint256) { + return SignedMath.abs(n); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SignedSafeMathMock.sol b/lib/openzeppelin-contracts/contracts/mocks/SignedSafeMathMock.sol new file mode 100644 index 0000000..8d10217 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SignedSafeMathMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SignedSafeMath.sol"; + +contract SignedSafeMathMock { + function mul(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.mul(a, b); + } + + function div(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.div(a, b); + } + + function sub(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.sub(a, b); + } + + function add(int256 a, int256 b) public pure returns (int256) { + return SignedSafeMath.add(a, b); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/SingleInheritanceInitializableMocks.sol b/lib/openzeppelin-contracts/contracts/mocks/SingleInheritanceInitializableMocks.sol new file mode 100644 index 0000000..6c82dd2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/SingleInheritanceInitializableMocks.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../proxy/utils/Initializable.sol"; + +/** + * @title MigratableMockV1 + * @dev This contract is a mock to test initializable functionality through migrations + */ +contract MigratableMockV1 is Initializable { + uint256 public x; + + function initialize(uint256 value) public payable initializer { + x = value; + } +} + +/** + * @title MigratableMockV2 + * @dev This contract is a mock to test migratable functionality with params + */ +contract MigratableMockV2 is MigratableMockV1 { + bool internal _migratedV2; + uint256 public y; + + function migrate(uint256 value, uint256 anotherValue) public payable { + require(!_migratedV2); + x = value; + y = anotherValue; + _migratedV2 = true; + } +} + +/** + * @title MigratableMockV3 + * @dev This contract is a mock to test migratable functionality without params + */ +contract MigratableMockV3 is MigratableMockV2 { + bool internal _migratedV3; + + function migrate() public payable { + require(!_migratedV3); + uint256 oldX = x; + x = y; + y = oldX; + _migratedV3 = true; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/StorageSlotMock.sol b/lib/openzeppelin-contracts/contracts/mocks/StorageSlotMock.sol new file mode 100644 index 0000000..5d099fc --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/StorageSlotMock.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/StorageSlot.sol"; + +contract StorageSlotMock { + using StorageSlot for bytes32; + + function setBoolean(bytes32 slot, bool value) public { + slot.getBooleanSlot().value = value; + } + + function setAddress(bytes32 slot, address value) public { + slot.getAddressSlot().value = value; + } + + function setBytes32(bytes32 slot, bytes32 value) public { + slot.getBytes32Slot().value = value; + } + + function setUint256(bytes32 slot, uint256 value) public { + slot.getUint256Slot().value = value; + } + + function getBoolean(bytes32 slot) public view returns (bool) { + return slot.getBooleanSlot().value; + } + + function getAddress(bytes32 slot) public view returns (address) { + return slot.getAddressSlot().value; + } + + function getBytes32(bytes32 slot) public view returns (bytes32) { + return slot.getBytes32Slot().value; + } + + function getUint256(bytes32 slot) public view returns (uint256) { + return slot.getUint256Slot().value; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/StringsMock.sol b/lib/openzeppelin-contracts/contracts/mocks/StringsMock.sol new file mode 100644 index 0000000..90a6c94 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/StringsMock.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Strings.sol"; + +contract StringsMock { + function toString(uint256 value) public pure returns (string memory) { + return Strings.toString(value); + } + + function toHexString(uint256 value) public pure returns (string memory) { + return Strings.toHexString(value); + } + + function toHexString(uint256 value, uint256 length) public pure returns (string memory) { + return Strings.toHexString(value, length); + } + + function toHexString(address addr) public pure returns (string memory) { + return Strings.toHexString(addr); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/TimersBlockNumberImpl.sol b/lib/openzeppelin-contracts/contracts/mocks/TimersBlockNumberImpl.sol new file mode 100644 index 0000000..84633e6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/TimersBlockNumberImpl.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Timers.sol"; + +contract TimersBlockNumberImpl { + using Timers for Timers.BlockNumber; + + Timers.BlockNumber private _timer; + + function getDeadline() public view returns (uint64) { + return _timer.getDeadline(); + } + + function setDeadline(uint64 timestamp) public { + _timer.setDeadline(timestamp); + } + + function reset() public { + _timer.reset(); + } + + function isUnset() public view returns (bool) { + return _timer.isUnset(); + } + + function isStarted() public view returns (bool) { + return _timer.isStarted(); + } + + function isPending() public view returns (bool) { + return _timer.isPending(); + } + + function isExpired() public view returns (bool) { + return _timer.isExpired(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/TimersTimestampImpl.sol b/lib/openzeppelin-contracts/contracts/mocks/TimersTimestampImpl.sol new file mode 100644 index 0000000..07f9a1b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/TimersTimestampImpl.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/Timers.sol"; + +contract TimersTimestampImpl { + using Timers for Timers.Timestamp; + + Timers.Timestamp private _timer; + + function getDeadline() public view returns (uint64) { + return _timer.getDeadline(); + } + + function setDeadline(uint64 timestamp) public { + _timer.setDeadline(timestamp); + } + + function reset() public { + _timer.reset(); + } + + function isUnset() public view returns (bool) { + return _timer.isUnset(); + } + + function isStarted() public view returns (bool) { + return _timer.isStarted(); + } + + function isPending() public view returns (bool) { + return _timer.isPending(); + } + + function isExpired() public view returns (bool) { + return _timer.isExpired(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/UUPS/UUPSLegacy.sol b/lib/openzeppelin-contracts/contracts/mocks/UUPS/UUPSLegacy.sol new file mode 100644 index 0000000..7a30028 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/UUPS/UUPSLegacy.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./UUPSUpgradeableMock.sol"; + +// This contract implements the pre-4.5 UUPS upgrade function with a rollback test. +// It's used to test that newer UUPS contracts are considered valid upgrades by older UUPS contracts. +contract UUPSUpgradeableLegacyMock is UUPSUpgradeableMock { + // Inlined from ERC1967Upgrade + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + // ERC1967Upgrade._setImplementation is private so we reproduce it here. + // An extra underscore prevents a name clash error. + function __setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + function _upgradeToAndCallSecureLegacyV1(address newImplementation, bytes memory data, bool forceCall) internal { + address oldImplementation = _getImplementation(); + + // Initial upgrade and setup call + __setImplementation(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + + // Perform rollback test if not already in progress + StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); + if (!rollbackTesting.value) { + // Trigger rollback using upgradeTo from the new implementation + rollbackTesting.value = true; + Address.functionDelegateCall( + newImplementation, + abi.encodeWithSignature("upgradeTo(address)", oldImplementation) + ); + rollbackTesting.value = false; + // Check rollback was effective + require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); + // Finally reset to the new implementation and log the upgrade + _upgradeTo(newImplementation); + } + } + + // hooking into the old mechanism + function upgradeTo(address newImplementation) external override { + _upgradeToAndCallSecureLegacyV1(newImplementation, bytes(""), false); + } + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable override { + _upgradeToAndCallSecureLegacyV1(newImplementation, data, false); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/UUPS/UUPSUpgradeableMock.sol b/lib/openzeppelin-contracts/contracts/mocks/UUPS/UUPSUpgradeableMock.sol new file mode 100644 index 0000000..35a74c3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/UUPS/UUPSUpgradeableMock.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../CountersImpl.sol"; +import "../../proxy/utils/UUPSUpgradeable.sol"; + +contract UUPSUpgradeableMock is CountersImpl, UUPSUpgradeable { + // Not having any checks in this function is dangerous! Do not do this outside tests! + function _authorizeUpgrade(address) internal override {} +} + +contract UUPSUpgradeableUnsafeMock is UUPSUpgradeableMock { + function upgradeTo(address newImplementation) external override { + ERC1967Upgrade._upgradeToAndCall(newImplementation, bytes(""), false); + } + + function upgradeToAndCall(address newImplementation, bytes memory data) external payable override { + ERC1967Upgrade._upgradeToAndCall(newImplementation, data, false); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/VotesMock.sol b/lib/openzeppelin-contracts/contracts/mocks/VotesMock.sol new file mode 100644 index 0000000..f888490 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/VotesMock.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../governance/utils/Votes.sol"; + +contract VotesMock is Votes { + mapping(address => uint256) private _balances; + mapping(uint256 => address) private _owners; + + constructor(string memory name) EIP712(name, "1") {} + + function getTotalSupply() public view returns (uint256) { + return _getTotalSupply(); + } + + function delegate(address account, address newDelegation) public { + return _delegate(account, newDelegation); + } + + function _getVotingUnits(address account) internal view override returns (uint256) { + return _balances[account]; + } + + function mint(address account, uint256 voteId) external { + _balances[account] += 1; + _owners[voteId] = account; + _transferVotingUnits(address(0), account, 1); + } + + function burn(uint256 voteId) external { + address owner = _owners[voteId]; + _balances[owner] -= 1; + _transferVotingUnits(owner, address(0), 1); + } + + function getChainId() external view returns (uint256) { + return block.chainid; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/compound/CompTimelock.sol b/lib/openzeppelin-contracts/contracts/mocks/compound/CompTimelock.sol new file mode 100644 index 0000000..49ffa4b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/compound/CompTimelock.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-3-Clause +// solhint-disable private-vars-leading-underscore +/** + * 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. + */ + +pragma solidity ^0.8.0; + +contract CompTimelock { + event NewAdmin(address indexed newAdmin); + event NewPendingAdmin(address indexed newPendingAdmin); + event NewDelay(uint256 indexed newDelay); + event CancelTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event ExecuteTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event QueueTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + + uint256 public constant GRACE_PERIOD = 14 days; + uint256 public constant MINIMUM_DELAY = 2 days; + uint256 public constant MAXIMUM_DELAY = 30 days; + + address public admin; + address public pendingAdmin; + uint256 public delay; + + mapping(bytes32 => bool) public queuedTransactions; + + constructor(address admin_, uint256 delay_) { + require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + + admin = admin_; + delay = delay_; + } + + receive() external payable {} + + function setDelay(uint256 delay_) public { + require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); + require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + delay = delay_; + + emit NewDelay(delay); + } + + function acceptAdmin() public { + require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); + admin = msg.sender; + pendingAdmin = address(0); + + emit NewAdmin(admin); + } + + function setPendingAdmin(address pendingAdmin_) public { + require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); + pendingAdmin = pendingAdmin_; + + emit NewPendingAdmin(pendingAdmin); + } + + function queueTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public returns (bytes32) { + require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); + require( + eta >= getBlockTimestamp() + delay, + "Timelock::queueTransaction: Estimated execution block must satisfy delay." + ); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = true; + + emit QueueTransaction(txHash, target, value, signature, data, eta); + return txHash; + } + + function cancelTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public { + require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = false; + + emit CancelTransaction(txHash, target, value, signature, data, eta); + } + + function executeTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) public payable returns (bytes memory) { + require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); + require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); + require(getBlockTimestamp() <= eta + GRACE_PERIOD, "Timelock::executeTransaction: Transaction is stale."); + + queuedTransactions[txHash] = false; + + bytes memory callData; + + if (bytes(signature).length == 0) { + callData = data; + } else { + callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); + } + + // solium-disable-next-line security/no-call-value + (bool success, bytes memory returnData) = target.call{value: value}(callData); + require(success, "Timelock::executeTransaction: Transaction execution reverted."); + + emit ExecuteTransaction(txHash, target, value, signature, data, eta); + + return returnData; + } + + function getBlockTimestamp() internal view returns (uint256) { + // solium-disable-next-line security/no-block-members + return block.timestamp; + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/crosschain/bridges.sol b/lib/openzeppelin-contracts/contracts/mocks/crosschain/bridges.sol new file mode 100644 index 0000000..41baffe --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/crosschain/bridges.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../../utils/Address.sol"; +import "../../vendor/polygon/IFxMessageProcessor.sol"; + +abstract contract BaseRelayMock { + // needed to parse custom errors + error NotCrossChainCall(); + error InvalidCrossChainSender(address sender, address expected); + + address internal _currentSender; + + function relayAs(address target, bytes calldata data, address sender) external virtual { + address previousSender = _currentSender; + + _currentSender = sender; + + (bool success, bytes memory returndata) = target.call(data); + Address.verifyCallResultFromTarget(target, success, returndata, "low-level call reverted"); + + _currentSender = previousSender; + } +} + +/** + * AMB + */ +contract BridgeAMBMock is BaseRelayMock { + function messageSender() public view returns (address) { + return _currentSender; + } +} + +/** + * Arbitrum + */ +contract BridgeArbitrumL1Mock is BaseRelayMock { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address public immutable inbox = address(new BridgeArbitrumL1Inbox()); + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address public immutable outbox = address(new BridgeArbitrumL1Outbox()); + + function activeOutbox() public view returns (address) { + return outbox; + } + + function currentSender() public view returns (address) { + return _currentSender; + } +} + +contract BridgeArbitrumL1Inbox { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address public immutable bridge = msg.sender; +} + +contract BridgeArbitrumL1Outbox { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address public immutable bridge = msg.sender; + + function l2ToL1Sender() public view returns (address) { + return BridgeArbitrumL1Mock(bridge).currentSender(); + } +} + +contract BridgeArbitrumL2Mock is BaseRelayMock { + function wasMyCallersAddressAliased() public view returns (bool) { + return _currentSender != address(0); + } + + function myCallersAddressWithoutAliasing() public view returns (address) { + return _currentSender; + } +} + +/** + * Optimism + */ +contract BridgeOptimismMock is BaseRelayMock { + function xDomainMessageSender() public view returns (address) { + return _currentSender; + } +} + +/** + * Polygon + */ +contract BridgePolygonChildMock is BaseRelayMock { + function relayAs(address target, bytes calldata data, address sender) external override { + IFxMessageProcessor(target).processMessageFromRoot(0, sender, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/crosschain/receivers.sol b/lib/openzeppelin-contracts/contracts/mocks/crosschain/receivers.sol new file mode 100644 index 0000000..601a2ac --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/crosschain/receivers.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +import "../../access/Ownable.sol"; +import "../../crosschain/amb/CrossChainEnabledAMB.sol"; +import "../../crosschain/arbitrum/CrossChainEnabledArbitrumL1.sol"; +import "../../crosschain/arbitrum/CrossChainEnabledArbitrumL2.sol"; +import "../../crosschain/optimism/CrossChainEnabledOptimism.sol"; +import "../../crosschain/polygon/CrossChainEnabledPolygonChild.sol"; + +abstract contract Receiver is CrossChainEnabled { + // we don't use Ownable because it messes up testing for the upgradeable contracts + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address public immutable owner = msg.sender; + + function crossChainRestricted() external onlyCrossChain {} + + function crossChainOwnerRestricted() external onlyCrossChainSender(owner) {} +} + +/** + * AMB + */ +contract CrossChainEnabledAMBMock is Receiver, CrossChainEnabledAMB { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address bridge) CrossChainEnabledAMB(bridge) {} +} + +/** + * Arbitrum + */ +contract CrossChainEnabledArbitrumL1Mock is Receiver, CrossChainEnabledArbitrumL1 { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address bridge) CrossChainEnabledArbitrumL1(bridge) {} +} + +contract CrossChainEnabledArbitrumL2Mock is Receiver, CrossChainEnabledArbitrumL2 {} + +/** + * Optimism + */ +contract CrossChainEnabledOptimismMock is Receiver, CrossChainEnabledOptimism { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address bridge) CrossChainEnabledOptimism(bridge) {} +} + +/** + * Polygon + */ +contract CrossChainEnabledPolygonChildMock is Receiver, CrossChainEnabledPolygonChild { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address bridge) CrossChainEnabledPolygonChild(bridge) {} +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor1.sol b/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor1.sol new file mode 100644 index 0000000..37ecfd5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor1.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/extensions/GovernorCountingSimple.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor1 is + Governor, + GovernorTimelockControl, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + IVotes _token, + TimelockController _timelock + ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + // The following functions are overrides required by Solidity. + + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor2.sol b/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor2.sol new file mode 100644 index 0000000..1472b67 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor2.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/extensions/GovernorProposalThreshold.sol"; +import "../../governance/extensions/GovernorCountingSimple.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor2 is + Governor, + GovernorTimelockControl, + GovernorProposalThreshold, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorCountingSimple +{ + constructor( + IVotes _token, + TimelockController _timelock + ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 1000e18; + } + + // The following functions are overrides required by Solidity. + + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorProposalThreshold, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor3.sol b/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor3.sol new file mode 100644 index 0000000..3203422 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/mocks/wizard/MyGovernor3.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "../../governance/Governor.sol"; +import "../../governance/compatibility/GovernorCompatibilityBravo.sol"; +import "../../governance/extensions/GovernorVotes.sol"; +import "../../governance/extensions/GovernorVotesQuorumFraction.sol"; +import "../../governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor is + Governor, + GovernorTimelockControl, + GovernorCompatibilityBravo, + GovernorVotes, + GovernorVotesQuorumFraction +{ + constructor( + IVotes _token, + TimelockController _timelock + ) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) {} + + function votingDelay() public pure override returns (uint256) { + return 1; // 1 block + } + + function votingPeriod() public pure override returns (uint256) { + return 45818; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 1000e18; + } + + // The following functions are overrides required by Solidity. + + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + return super.quorum(blockNumber); + } + + function state( + uint256 proposalId + ) public view override(Governor, IGovernor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, GovernorCompatibilityBravo, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, IERC165, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/package.json b/lib/openzeppelin-contracts/contracts/package.json new file mode 100644 index 0000000..e3e6083 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/package.json @@ -0,0 +1,32 @@ +{ + "name": "@openzeppelin/contracts", + "description": "Secure Smart Contract library for Solidity", + "version": "4.8.0", + "files": [ + "**/*.sol", + "/build/contracts/*.json", + "!/mocks/**/*" + ], + "scripts": { + "prepare": "bash ../scripts/prepare-contracts-package.sh", + "prepare-docs": "cd ..; npm run prepare-docs" + }, + "repository": { + "type": "git", + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git" + }, + "keywords": [ + "solidity", + "ethereum", + "smart", + "contracts", + "security", + "zeppelin" + ], + "author": "OpenZeppelin Community ", + "license": "MIT", + "bugs": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/issues" + }, + "homepage": "https://openzeppelin.com/contracts/" +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/Clones.sol b/lib/openzeppelin-contracts/contracts/proxy/Clones.sol new file mode 100644 index 0000000..7125198 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/Clones.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol) + +pragma solidity ^0.8.0; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for + * deploying minimal proxy contracts, also known as "clones". + * + * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies + * > a minimal bytecode implementation that delegates all calls to a known, fixed address. + * + * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` + * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the + * deterministic method. + * + * _Available since v3.4._ + */ +library Clones { + /** + * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. + * + * This function uses the create opcode, which should never revert. + */ + function clone(address implementation) internal returns (address instance) { + /// @solidity memory-safe-assembly + assembly { + // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes + // of the `implementation` address with the bytecode before the address. + mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) + // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. + mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) + instance := create(0, 0x09, 0x37) + } + require(instance != address(0), "ERC1167: create failed"); + } + + /** + * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. + * + * This function uses the create2 opcode and a `salt` to deterministically deploy + * the clone. Using the same `implementation` and `salt` multiple time will revert, since + * the clones cannot be deployed twice at the same address. + */ + function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { + /// @solidity memory-safe-assembly + assembly { + // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes + // of the `implementation` address with the bytecode before the address. + mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) + // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. + mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) + instance := create2(0, 0x09, 0x37, salt) + } + require(instance != address(0), "ERC1167: create2 failed"); + } + + /** + * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. + */ + function predictDeterministicAddress( + address implementation, + bytes32 salt, + address deployer + ) internal pure returns (address predicted) { + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) + mstore(add(ptr, 0x38), deployer) + mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) + mstore(add(ptr, 0x14), implementation) + mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) + mstore(add(ptr, 0x58), salt) + mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) + predicted := keccak256(add(ptr, 0x43), 0x55) + } + } + + /** + * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. + */ + function predictDeterministicAddress( + address implementation, + bytes32 salt + ) internal view returns (address predicted) { + return predictDeterministicAddress(implementation, salt, address(this)); + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol b/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol new file mode 100644 index 0000000..a04d701 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) + +pragma solidity ^0.8.0; + +import "../Proxy.sol"; +import "./ERC1967Upgrade.sol"; + +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + */ +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializing the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { + _upgradeToAndCall(_logic, _data, false); + } + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + return ERC1967Upgrade._getImplementation(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Upgrade.sol b/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Upgrade.sol new file mode 100644 index 0000000..0f32d4d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Upgrade.sol @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol) + +pragma solidity ^0.8.2; + +import "../beacon/IBeacon.sol"; +import "../../interfaces/draft-IERC1822.sol"; +import "../../utils/Address.sol"; +import "../../utils/StorageSlot.sol"; + +/** + * @dev This abstract contract provides getters and event emitting update functions for + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. + * + * _Available since v4.1._ + * + * @custom:oz-upgrades-unsafe-allow delegatecall + */ +abstract contract ERC1967Upgrade { + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Emitted when the implementation is upgraded. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Returns the current implementation address. + */ + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 implementation slot. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + /** + * @dev Perform implementation upgrade + * + * Emits an {Upgraded} event. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Perform implementation upgrade with additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal { + _upgradeTo(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + } + + /** + * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal { + // Upgrades from old implementations will perform a rollback test. This test requires the new + // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing + // this special case will break upgrade paths from old UUPS implementation to new ones. + if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { + _setImplementation(newImplementation); + } else { + try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { + require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); + } catch { + revert("ERC1967Upgrade: new implementation is not UUPS"); + } + _upgradeToAndCall(newImplementation, data, forceCall); + } + } + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Returns the current admin. + */ + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + */ + function _changeAdmin(address newAdmin) internal { + emit AdminChanged(_getAdmin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Emitted when the beacon is upgraded. + */ + event BeaconUpgraded(address indexed beacon); + + /** + * @dev Returns the current beacon. + */ + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + /** + * @dev Stores a new beacon in the EIP1967 beacon slot. + */ + function _setBeacon(address newBeacon) private { + require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } + + /** + * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does + * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). + * + * Emits a {BeaconUpgraded} event. + */ + function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal { + _setBeacon(newBeacon); + emit BeaconUpgraded(newBeacon); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/Proxy.sol b/lib/openzeppelin-contracts/contracts/proxy/Proxy.sol new file mode 100644 index 0000000..988cf72 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/Proxy.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM + * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to + * be specified by overriding the virtual {_implementation} function. + * + * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a + * different contract through the {_delegate} function. + * + * The success and return data of the delegated call will be returned back to the caller of the proxy. + */ +abstract contract Proxy { + /** + * @dev Delegates the current call to `implementation`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _delegate(address implementation) internal virtual { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function + * and {_fallback} should delegate. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates the current call to the address returned by `_implementation()`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable virtual { + _fallback(); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data + * is empty. + */ + receive() external payable virtual { + _fallback(); + } + + /** + * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` + * call, or as part of the Solidity `fallback` or `receive` functions. + * + * If overridden should call `super._beforeFallback()`. + */ + function _beforeFallback() internal virtual {} +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/README.adoc b/lib/openzeppelin-contracts/contracts/proxy/README.adoc new file mode 100644 index 0000000..5ada16e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/README.adoc @@ -0,0 +1,85 @@ += Proxies + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/proxy + +This is a low-level set of contracts implementing different proxy patterns with and without upgradeability. For an in-depth overview of this pattern check out the xref:upgrades-plugins::proxies.adoc[Proxy Upgrade Pattern] page. + +Most of the proxies below are built on an abstract base contract. + +- {Proxy}: Abstract contract implementing the core delegation functionality. + +In order to avoid clashes with the storage variables of the implementation contract behind a proxy, we use https://eips.ethereum.org/EIPS/eip-1967[EIP1967] storage slots. + +- {ERC1967Upgrade}: Internal functions to get and set the storage slots defined in EIP1967. +- {ERC1967Proxy}: A proxy using EIP1967 storage slots. Not upgradeable by default. + +There are two alternative ways to add upgradeability to an ERC1967 proxy. Their differences are explained below in <>. + +- {TransparentUpgradeableProxy}: A proxy with a built in admin and upgrade interface. +- {UUPSUpgradeable}: An upgradeability mechanism to be included in the implementation contract. + +CAUTION: Using upgradeable proxies correctly and securely is a difficult task that requires deep knowledge of the proxy pattern, Solidity, and the EVM. Unless you want a lot of low level control, we recommend using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins] for Truffle and Hardhat. + +A different family of proxies are beacon proxies. This pattern, popularized by Dharma, allows multiple proxies to be upgraded to a different implementation in a single transaction. + +- {BeaconProxy}: A proxy that retrieves its implementation from a beacon contract. +- {UpgradeableBeacon}: A beacon contract with a built in admin that can upgrade the {BeaconProxy} pointing to it. + +In this pattern, the proxy contract doesn't hold the implementation address in storage like an ERC1967 proxy. Instead, the address is stored in a separate beacon contract. The `upgrade` operations are sent to the beacon instead of to the proxy contract, and all proxies that follow that beacon are automatically upgraded. + +Outside the realm of upgradeability, proxies can also be useful to make cheap contract clones, such as those created by an on-chain factory contract that creates many instances of the same contract. These instances are designed to be both cheap to deploy, and cheap to call. + +- {Clones}: A library that can deploy cheap minimal non-upgradeable proxies. + +[[transparent-vs-uups]] +== Transparent vs UUPS Proxies + +The original proxies included in OpenZeppelin followed the https://blog.openzeppelin.com/the-transparent-proxy-pattern/[Transparent Proxy Pattern]. While this pattern is still provided, our recommendation is now shifting towards UUPS proxies, which are both lightweight and versatile. The name UUPS comes from https://eips.ethereum.org/EIPS/eip-1822[EIP1822], which first documented the pattern. + +While both of these share the same interface for upgrades, in UUPS proxies the upgrade is handled by the implementation, and can eventually be removed. Transparent proxies, on the other hand, include the upgrade and admin logic in the proxy itself. This means {TransparentUpgradeableProxy} is more expensive to deploy than what is possible with UUPS proxies. + +UUPS proxies are implemented using an {ERC1967Proxy}. Note that this proxy is not by itself upgradeable. It is the role of the implementation to include, alongside the contract's logic, all the code necessary to update the implementation's address that is stored at a specific slot in the proxy's storage space. This is where the {UUPSUpgradeable} contract comes in. Inheriting from it (and overriding the {xref-UUPSUpgradeable-_authorizeUpgrade-address-}[`_authorizeUpgrade`] function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation. + +Note that since both proxies use the same storage slot for the implementation address, using a UUPS compliant implementation with a {TransparentUpgradeableProxy} might allow non-admins to perform upgrade operations. + +By default, the upgrade functionality included in {UUPSUpgradeable} contains a security mechanism that will prevent any upgrades to a non UUPS compliant implementation. This prevents upgrades to an implementation contract that wouldn't contain the necessary upgrade mechanism, as it would lock the upgradeability of the proxy forever. This security mechanism can be bypassed by either of: + +- Adding a flag mechanism in the implementation that will disable the upgrade function when triggered. +- Upgrading to an implementation that features an upgrade mechanism without the additional security check, and then upgrading again to another implementation without the upgrade mechanism. + +The current implementation of this security mechanism uses https://eips.ethereum.org/EIPS/eip-1822[EIP1822] to detect the storage slot used by the implementation. A previous implementation, now deprecated, relied on a rollback check. It is possible to upgrade from a contract using the old mechanism to a new one. The inverse is however not possible, as old implementations (before version 4.5) did not include the `ERC1822` interface. + +== Core + +{{Proxy}} + +== ERC1967 + +{{ERC1967Proxy}} + +{{ERC1967Upgrade}} + +== Transparent Proxy + +{{TransparentUpgradeableProxy}} + +{{ProxyAdmin}} + +== Beacon + +{{BeaconProxy}} + +{{IBeacon}} + +{{UpgradeableBeacon}} + +== Minimal Clones + +{{Clones}} + +== Utils + +{{Initializable}} + +{{UUPSUpgradeable}} diff --git a/lib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol b/lib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol new file mode 100644 index 0000000..d217b15 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol) + +pragma solidity ^0.8.0; + +import "./IBeacon.sol"; +import "../Proxy.sol"; +import "../ERC1967/ERC1967Upgrade.sol"; + +/** + * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}. + * + * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't + * conflict with the storage layout of the implementation behind the proxy. + * + * _Available since v3.4._ + */ +contract BeaconProxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the proxy with `beacon`. + * + * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This + * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity + * constructor. + * + * Requirements: + * + * - `beacon` must be a contract with the interface {IBeacon}. + */ + constructor(address beacon, bytes memory data) payable { + _upgradeBeaconToAndCall(beacon, data, false); + } + + /** + * @dev Returns the current beacon address. + */ + function _beacon() internal view virtual returns (address) { + return _getBeacon(); + } + + /** + * @dev Returns the current implementation address of the associated beacon. + */ + function _implementation() internal view virtual override returns (address) { + return IBeacon(_getBeacon()).implementation(); + } + + /** + * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}. + * + * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. + * + * Requirements: + * + * - `beacon` must be a contract. + * - The implementation returned by `beacon` must be a contract. + */ + function _setBeacon(address beacon, bytes memory data) internal virtual { + _upgradeBeaconToAndCall(beacon, data, false); + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol b/lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol new file mode 100644 index 0000000..fba3ee2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) + +pragma solidity ^0.8.0; + +/** + * @dev This is the interface that {BeaconProxy} expects of its beacon. + */ +interface IBeacon { + /** + * @dev Must return an address that can be used as a delegate call target. + * + * {BeaconProxy} will check that this address is a contract. + */ + function implementation() external view returns (address); +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol b/lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol new file mode 100644 index 0000000..5d83ceb --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol) + +pragma solidity ^0.8.0; + +import "./IBeacon.sol"; +import "../../access/Ownable.sol"; +import "../../utils/Address.sol"; + +/** + * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their + * implementation contract, which is where they will delegate all function calls. + * + * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon. + */ +contract UpgradeableBeacon is IBeacon, Ownable { + address private _implementation; + + /** + * @dev Emitted when the implementation returned by the beacon is changed. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the + * beacon. + */ + constructor(address implementation_) { + _setImplementation(implementation_); + } + + /** + * @dev Returns the current implementation address. + */ + function implementation() public view virtual override returns (address) { + return _implementation; + } + + /** + * @dev Upgrades the beacon to a new implementation. + * + * Emits an {Upgraded} event. + * + * Requirements: + * + * - msg.sender must be the owner of the contract. + * - `newImplementation` must be a contract. + */ + function upgradeTo(address newImplementation) public virtual onlyOwner { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation contract address for this beacon + * + * Requirements: + * + * - `newImplementation` must be a contract. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract"); + _implementation = newImplementation; + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol b/lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol new file mode 100644 index 0000000..8395342 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol) + +pragma solidity ^0.8.0; + +import "./TransparentUpgradeableProxy.sol"; +import "../../access/Ownable.sol"; + +/** + * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an + * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. + */ +contract ProxyAdmin is Ownable { + /** + * @dev Returns the current implementation of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("implementation()")) == 0x5c60da1b + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Returns the current admin of `proxy`. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) { + // We need to manually run the static call since the getter cannot be flagged as view + // bytes4(keccak256("admin()")) == 0xf851a440 + (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); + require(success); + return abi.decode(returndata, (address)); + } + + /** + * @dev Changes the admin of `proxy` to `newAdmin`. + * + * Requirements: + * + * - This contract must be the current admin of `proxy`. + */ + function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { + proxy.changeAdmin(newAdmin); + } + + /** + * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { + proxy.upgradeTo(implementation); + } + + /** + * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See + * {TransparentUpgradeableProxy-upgradeToAndCall}. + * + * Requirements: + * + * - This contract must be the admin of `proxy`. + */ + function upgradeAndCall( + TransparentUpgradeableProxy proxy, + address implementation, + bytes memory data + ) public payable virtual onlyOwner { + proxy.upgradeToAndCall{value: msg.value}(implementation, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol b/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol new file mode 100644 index 0000000..3685360 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol) + +pragma solidity ^0.8.0; + +import "../ERC1967/ERC1967Proxy.sol"; + +/** + * @dev This contract implements a proxy that is upgradeable by an admin. + * + * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector + * clashing], which can potentially be used in an attack, this contract uses the + * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two + * things that go hand in hand: + * + * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if + * that call matches one of the admin functions exposed by the proxy itself. + * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the + * implementation. If the admin tries to call a function on the implementation it will fail with an error that says + * "admin cannot fallback to proxy target". + * + * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing + * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due + * to sudden errors when trying to call a function from the proxy implementation. + * + * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, + * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. + */ +contract TransparentUpgradeableProxy is ERC1967Proxy { + /** + * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and + * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. + */ + constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) { + _changeAdmin(admin_); + } + + /** + * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. + */ + modifier ifAdmin() { + if (msg.sender == _getAdmin()) { + _; + } else { + _fallback(); + } + } + + /** + * @dev Returns the current admin. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + */ + function admin() external ifAdmin returns (address admin_) { + admin_ = _getAdmin(); + } + + /** + * @dev Returns the current implementation. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. + * + * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the + * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. + * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + */ + function implementation() external ifAdmin returns (address implementation_) { + implementation_ = _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}. + */ + function changeAdmin(address newAdmin) external virtual ifAdmin { + _changeAdmin(newAdmin); + } + + /** + * @dev Upgrade the implementation of the proxy. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeToAndCall(newImplementation, bytes(""), false); + } + + /** + * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified + * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the + * proxied contract. + * + * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin { + _upgradeToAndCall(newImplementation, data, true); + } + + /** + * @dev Returns the current admin. + */ + function _admin() internal view virtual returns (address) { + return _getAdmin(); + } + + /** + * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}. + */ + function _beforeFallback() internal virtual override { + require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target"); + super._beforeFallback(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol b/lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol new file mode 100644 index 0000000..b454b5d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol) + +pragma solidity ^0.8.2; + +import "../../utils/Address.sol"; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be + * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in + * case an upgrade adds a module that needs to be initialized. + * + * For example: + * + * [.hljs-theme-light.nopadding] + * ``` + * contract MyToken is ERC20Upgradeable { + * function initialize() initializer public { + * __ERC20_init("MyToken", "MTK"); + * } + * } + * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { + * function initializeV2() reinitializer(2) public { + * __ERC20Permit_init("MyToken"); + * } + * } + * ``` + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke + * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() { + * _disableInitializers(); + * } + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + * @custom:oz-retyped-from bool + */ + uint8 private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Triggered when the contract has been initialized or reinitialized. + */ + event Initialized(uint8 version); + + /** + * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, + * `onlyInitializing` functions can be used to initialize parent contracts. + * + * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a + * constructor. + * + * Emits an {Initialized} event. + */ + modifier initializer() { + bool isTopLevelCall = !_initializing; + require( + (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), + "Initializable: contract is already initialized" + ); + _initialized = 1; + if (isTopLevelCall) { + _initializing = true; + } + _; + if (isTopLevelCall) { + _initializing = false; + emit Initialized(1); + } + } + + /** + * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the + * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be + * used to initialize parent contracts. + * + * A reinitializer may be used after the original initialization step. This is essential to configure modules that + * are added through upgrades and that require initialization. + * + * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` + * cannot be nested. If one is invoked in the context of another, execution will revert. + * + * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in + * a contract, executing them in the right order is up to the developer or operator. + * + * WARNING: setting the version to 255 will prevent any future reinitialization. + * + * Emits an {Initialized} event. + */ + modifier reinitializer(uint8 version) { + require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); + _initialized = version; + _initializing = true; + _; + _initializing = false; + emit Initialized(version); + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} and {reinitializer} modifiers, directly or indirectly. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + + /** + * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. + * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized + * to any version. It is recommended to use this to lock implementation contracts that are designed to be called + * through proxies. + * + * Emits an {Initialized} event the first time it is successfully executed. + */ + function _disableInitializers() internal virtual { + require(!_initializing, "Initializable: contract is initializing"); + if (_initialized != type(uint8).max) { + _initialized = type(uint8).max; + emit Initialized(type(uint8).max); + } + } + + /** + * @dev Returns the highest version that has been initialized. See {reinitializer}. + */ + function _getInitializedVersion() internal view returns (uint8) { + return _initialized; + } + + /** + * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. + */ + function _isInitializing() internal view returns (bool) { + return _initializing; + } +} diff --git a/lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol b/lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol new file mode 100644 index 0000000..8b73663 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/UUPSUpgradeable.sol) + +pragma solidity ^0.8.0; + +import "../../interfaces/draft-IERC1822.sol"; +import "../ERC1967/ERC1967Upgrade.sol"; + +/** + * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an + * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. + * + * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is + * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing + * `UUPSUpgradeable` with a custom implementation of upgrades. + * + * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. + * + * _Available since v4.1._ + */ +abstract contract UUPSUpgradeable is IERC1822Proxiable, ERC1967Upgrade { + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment + address private immutable __self = address(this); + + /** + * @dev Check that the execution is being performed through a delegatecall call and that the execution context is + * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case + * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a + * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to + * fail. + */ + modifier onlyProxy() { + require(address(this) != __self, "Function must be called through delegatecall"); + require(_getImplementation() == __self, "Function must be called through active proxy"); + _; + } + + /** + * @dev Check that the execution is not being performed through a delegate call. This allows a function to be + * callable on the implementing contract but not through proxies. + */ + modifier notDelegated() { + require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall"); + _; + } + + /** + * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the + * implementation. It is used to validate the implementation's compatibility when performing an upgrade. + * + * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks + * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this + * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. + */ + function proxiableUUID() external view virtual override notDelegated returns (bytes32) { + return _IMPLEMENTATION_SLOT; + } + + /** + * @dev Upgrade the implementation of the proxy to `newImplementation`. + * + * Calls {_authorizeUpgrade}. + * + * Emits an {Upgraded} event. + */ + function upgradeTo(address newImplementation) external virtual onlyProxy { + _authorizeUpgrade(newImplementation); + _upgradeToAndCallUUPS(newImplementation, new bytes(0), false); + } + + /** + * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call + * encoded in `data`. + * + * Calls {_authorizeUpgrade}. + * + * Emits an {Upgraded} event. + */ + function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { + _authorizeUpgrade(newImplementation); + _upgradeToAndCallUUPS(newImplementation, data, true); + } + + /** + * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by + * {upgradeTo} and {upgradeToAndCall}. + * + * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. + * + * ```solidity + * function _authorizeUpgrade(address) internal override onlyOwner {} + * ``` + */ + function _authorizeUpgrade(address newImplementation) internal virtual; +} diff --git a/lib/openzeppelin-contracts/contracts/security/Pausable.sol b/lib/openzeppelin-contracts/contracts/security/Pausable.sol new file mode 100644 index 0000000..bdd1184 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/security/Pausable.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) + +pragma solidity ^0.8.0; + +import "../utils/Context.sol"; + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + bool private _paused; + + /** + * @dev Initializes the contract in unpaused state. + */ + constructor() { + _paused = false; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + _requireNotPaused(); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + _requirePaused(); + _; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Throws if the contract is paused. + */ + function _requireNotPaused() internal view virtual { + require(!paused(), "Pausable: paused"); + } + + /** + * @dev Throws if the contract is not paused. + */ + function _requirePaused() internal view virtual { + require(paused(), "Pausable: not paused"); + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} diff --git a/lib/openzeppelin-contracts/contracts/security/PullPayment.sol b/lib/openzeppelin-contracts/contracts/security/PullPayment.sol new file mode 100644 index 0000000..65b4980 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/security/PullPayment.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (security/PullPayment.sol) + +pragma solidity ^0.8.0; + +import "../utils/escrow/Escrow.sol"; + +/** + * @dev Simple implementation of a + * https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/external-calls/#favor-pull-over-push-for-external-calls[pull-payment] + * strategy, where the paying contract doesn't interact directly with the + * receiver account, which must withdraw its payments itself. + * + * Pull-payments are often considered the best practice when it comes to sending + * Ether, security-wise. It prevents recipients from blocking execution, and + * eliminates reentrancy concerns. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + * + * To use, derive from the `PullPayment` contract, and use {_asyncTransfer} + * instead of Solidity's `transfer` function. Payees can query their due + * payments with {payments}, and retrieve them with {withdrawPayments}. + */ +abstract contract PullPayment { + Escrow private immutable _escrow; + + constructor() { + _escrow = new Escrow(); + } + + /** + * @dev Withdraw accumulated payments, forwarding all gas to the recipient. + * + * Note that _any_ account can call this function, not just the `payee`. + * This means that contracts unaware of the `PullPayment` protocol can still + * receive funds this way, by having a separate account call + * {withdrawPayments}. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee Whose payments will be withdrawn. + * + * Causes the `escrow` to emit a {Withdrawn} event. + */ + function withdrawPayments(address payable payee) public virtual { + _escrow.withdraw(payee); + } + + /** + * @dev Returns the payments owed to an address. + * @param dest The creditor's address. + */ + function payments(address dest) public view returns (uint256) { + return _escrow.depositsOf(dest); + } + + /** + * @dev Called by the payer to store the sent amount as credit to be pulled. + * Funds sent in this way are stored in an intermediate {Escrow} contract, so + * there is no danger of them being spent before withdrawal. + * + * @param dest The destination address of the funds. + * @param amount The amount to transfer. + * + * Causes the `escrow` to emit a {Deposited} event. + */ + function _asyncTransfer(address dest, uint256 amount) internal virtual { + _escrow.deposit{value: amount}(dest); + } +} diff --git a/lib/openzeppelin-contracts/contracts/security/README.adoc b/lib/openzeppelin-contracts/contracts/security/README.adoc new file mode 100644 index 0000000..66f398f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/security/README.adoc @@ -0,0 +1,20 @@ += Security + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/security + +These contracts aim to cover common security practices. + +* {PullPayment}: A pattern that can be used to avoid reentrancy attacks. +* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions. +* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending. + +TIP: For an overview on reentrancy and the possible mechanisms to prevent it, read our article https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + +== Contracts + +{{PullPayment}} + +{{ReentrancyGuard}} + +{{Pausable}} diff --git a/lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol b/lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol new file mode 100644 index 0000000..f9281ec --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuard { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant _NOT_ENTERED = 1; + uint256 private constant _ENTERED = 2; + + uint256 private _status; + + constructor() { + _status = _NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and making it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + _nonReentrantBefore(); + _; + _nonReentrantAfter(); + } + + function _nonReentrantBefore() private { + // On the first call to nonReentrant, _status will be _NOT_ENTERED + require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); + + // Any calls to nonReentrant after this point will fail + _status = _ENTERED; + } + + function _nonReentrantAfter() private { + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + _status = _NOT_ENTERED; + } + + /** + * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a + * `nonReentrant` function in the call stack. + */ + function _reentrancyGuardEntered() internal view returns (bool) { + return _status == _ENTERED; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol new file mode 100644 index 0000000..b20b711 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sol @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/ERC1155.sol) + +pragma solidity ^0.8.0; + +import "./IERC1155.sol"; +import "./IERC1155Receiver.sol"; +import "./extensions/IERC1155MetadataURI.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of the basic standard multi-token. + * See https://eips.ethereum.org/EIPS/eip-1155 + * Originally based on code by Enjin: https://github.com/enjin/erc-1155 + * + * _Available since v3.1._ + */ +contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { + using Address for address; + + // Mapping from token ID to account balances + mapping(uint256 => mapping(address => uint256)) private _balances; + + // Mapping from account to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json + string private _uri; + + /** + * @dev See {_setURI}. + */ + constructor(string memory uri_) { + _setURI(uri_); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC1155).interfaceId || + interfaceId == type(IERC1155MetadataURI).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC1155MetadataURI-uri}. + * + * This implementation returns the same URI for *all* token types. It relies + * on the token type ID substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * Clients calling this function must replace the `\{id\}` substring with the + * actual token type ID. + */ + function uri(uint256) public view virtual override returns (string memory) { + return _uri; + } + + /** + * @dev See {IERC1155-balanceOf}. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { + require(account != address(0), "ERC1155: address zero is not a valid owner"); + return _balances[id][account]; + } + + /** + * @dev See {IERC1155-balanceOfBatch}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch( + address[] memory accounts, + uint256[] memory ids + ) public view virtual override returns (uint256[] memory) { + require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); + + uint256[] memory batchBalances = new uint256[](accounts.length); + + for (uint256 i = 0; i < accounts.length; ++i) { + batchBalances[i] = balanceOf(accounts[i], ids[i]); + } + + return batchBalances; + } + + /** + * @dev See {IERC1155-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC1155-isApprovedForAll}. + */ + function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { + return _operatorApprovals[account][operator]; + } + + /** + * @dev See {IERC1155-safeTransferFrom}. + */ + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual override { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()), + "ERC1155: caller is not token owner or approved" + ); + _safeTransferFrom(from, to, id, amount, data); + } + + /** + * @dev See {IERC1155-safeBatchTransferFrom}. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual override { + require( + from == _msgSender() || isApprovedForAll(from, _msgSender()), + "ERC1155: caller is not token owner or approved" + ); + _safeBatchTransferFrom(from, to, ids, amounts, data); + } + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: transfer to the zero address"); + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + uint256[] memory amounts = _asSingletonArray(amount); + + _beforeTokenTransfer(operator, from, to, ids, amounts, data); + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + _balances[id][to] += amount; + + emit TransferSingle(operator, from, to, id, amount); + + _afterTokenTransfer(operator, from, to, ids, amounts, data); + + _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + require(to != address(0), "ERC1155: transfer to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, to, ids, amounts, data); + + for (uint256 i = 0; i < ids.length; ++i) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + _balances[id][to] += amount; + } + + emit TransferBatch(operator, from, to, ids, amounts); + + _afterTokenTransfer(operator, from, to, ids, amounts, data); + + _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); + } + + /** + * @dev Sets a new URI for all token types, by relying on the token type ID + * substitution mechanism + * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. + * + * By this mechanism, any occurrence of the `\{id\}` substring in either the + * URI or any of the amounts in the JSON file at said URI will be replaced by + * clients with the token type ID. + * + * For example, the `https://token-cdn-domain/\{id\}.json` URI would be + * interpreted by clients as + * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` + * for token type ID 0x4cce0. + * + * See {uri}. + * + * Because these URIs cannot be meaningfully represented by the {URI} event, + * this function emits no events. + */ + function _setURI(string memory newuri) internal virtual { + _uri = newuri; + } + + /** + * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual { + require(to != address(0), "ERC1155: mint to the zero address"); + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + uint256[] memory amounts = _asSingletonArray(amount); + + _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); + + _balances[id][to] += amount; + emit TransferSingle(operator, address(0), to, id, amount); + + _afterTokenTransfer(operator, address(0), to, ids, amounts, data); + + _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function _mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual { + require(to != address(0), "ERC1155: mint to the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); + + for (uint256 i = 0; i < ids.length; i++) { + _balances[ids[i]][to] += amounts[i]; + } + + emit TransferBatch(operator, address(0), to, ids, amounts); + + _afterTokenTransfer(operator, address(0), to, ids, amounts, data); + + _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); + } + + /** + * @dev Destroys `amount` tokens of token type `id` from `from` + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `from` must have at least `amount` tokens of token type `id`. + */ + function _burn(address from, uint256 id, uint256 amount) internal virtual { + require(from != address(0), "ERC1155: burn from the zero address"); + + address operator = _msgSender(); + uint256[] memory ids = _asSingletonArray(id); + uint256[] memory amounts = _asSingletonArray(amount); + + _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + + emit TransferSingle(operator, from, address(0), id, amount); + + _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + */ + function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual { + require(from != address(0), "ERC1155: burn from the zero address"); + require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); + + for (uint256 i = 0; i < ids.length; i++) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + + uint256 fromBalance = _balances[id][from]; + require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); + unchecked { + _balances[id][from] = fromBalance - amount; + } + } + + emit TransferBatch(operator, from, address(0), ids, amounts); + + _afterTokenTransfer(operator, from, address(0), ids, amounts, ""); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits an {ApprovalForAll} event. + */ + function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { + require(owner != operator, "ERC1155: setting approval status for self"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Hook that is called before any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `ids` and `amounts` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual {} + + /** + * @dev Hook that is called after any token transfer. This includes minting + * and burning, as well as batched variants. + * + * The same hook is called on both single and batched variants. For single + * transfers, the length of the `id` and `amount` arrays will be 1. + * + * Calling conditions (for each `id` and `amount` pair): + * + * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * of token type `id` will be transferred to `to`. + * - When `from` is zero, `amount` tokens of token type `id` will be minted + * for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` + * will be burned. + * - `from` and `to` are never both zero. + * - `ids` and `amounts` have the same, non-zero length. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual {} + + function _doSafeTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { + if (response != IERC1155Receiver.onERC1155Received.selector) { + revert("ERC1155: ERC1155Receiver rejected tokens"); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert("ERC1155: transfer to non-ERC1155Receiver implementer"); + } + } + } + + function _doSafeBatchTransferAcceptanceCheck( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) private { + if (to.isContract()) { + try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( + bytes4 response + ) { + if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { + revert("ERC1155: ERC1155Receiver rejected tokens"); + } + } catch Error(string memory reason) { + revert(reason); + } catch { + revert("ERC1155: transfer to non-ERC1155Receiver implementer"); + } + } + } + + function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { + uint256[] memory array = new uint256[](1); + array[0] = element; + + return array; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol new file mode 100644 index 0000000..eae0b70 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC1155/IERC1155.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC1155 compliant contract, as defined in the + * https://eips.ethereum.org/EIPS/eip-1155[EIP]. + * + * _Available since v3.1._ + */ +interface IERC1155 is IERC165 { + /** + * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. + */ + event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); + + /** + * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all + * transfers. + */ + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] values + ); + + /** + * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to + * `approved`. + */ + event ApprovalForAll(address indexed account, address indexed operator, bool approved); + + /** + * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. + * + * If an {URI} event was emitted for `id`, the standard + * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value + * returned by {IERC1155MetadataURI-uri}. + */ + event URI(string value, uint256 indexed id); + + /** + * @dev Returns the amount of tokens of token type `id` owned by `account`. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function balanceOf(address account, uint256 id) external view returns (uint256); + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. + * + * Requirements: + * + * - `accounts` and `ids` must have the same length. + */ + function balanceOfBatch( + address[] calldata accounts, + uint256[] calldata ids + ) external view returns (uint256[] memory); + + /** + * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, + * + * Emits an {ApprovalForAll} event. + * + * Requirements: + * + * - `operator` cannot be the caller. + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. + * + * See {setApprovalForAll}. + */ + function isApprovedForAll(address account, address operator) external view returns (bool); + + /** + * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. + * + * Emits a {TransferSingle} event. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}. + * - `from` must have a balance of tokens of type `id` of at least `amount`. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the + * acceptance magic value. + */ + function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. + * + * Emits a {TransferBatch} event. + * + * Requirements: + * + * - `ids` and `amounts` must have the same length. + * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the + * acceptance magic value. + */ + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) external; +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol new file mode 100644 index 0000000..0dd271d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev _Available since v3.1._ + */ +interface IERC1155Receiver is IERC165 { + /** + * @dev Handles the receipt of a single ERC1155 token type. This function is + * called at the end of a `safeTransferFrom` after the balance has been updated. + * + * NOTE: To accept the transfer, this must return + * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` + * (i.e. 0xf23a6e61, or its own function selector). + * + * @param operator The address which initiated the transfer (i.e. msg.sender) + * @param from The address which previously owned the token + * @param id The ID of the token being transferred + * @param value The amount of tokens being transferred + * @param data Additional data with no specified format + * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed + */ + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external returns (bytes4); + + /** + * @dev Handles the receipt of a multiple ERC1155 token types. This function + * is called at the end of a `safeBatchTransferFrom` after the balances have + * been updated. + * + * NOTE: To accept the transfer(s), this must return + * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` + * (i.e. 0xbc197c81, or its own function selector). + * + * @param operator The address which initiated the batch transfer (i.e. msg.sender) + * @param from The address which previously owned the token + * @param ids An array containing ids of each token being transferred (order and length must match values array) + * @param values An array containing amounts of each token being transferred (order and length must match ids array) + * @param data Additional data with no specified format + * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed + */ + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external returns (bytes4); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/README.adoc b/lib/openzeppelin-contracts/contracts/token/ERC1155/README.adoc new file mode 100644 index 0000000..13ffbdb --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/README.adoc @@ -0,0 +1,49 @@ += ERC 1155 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc1155 + +This set of interfaces and contracts are all related to the https://eips.ethereum.org/EIPS/eip-1155[ERC1155 Multi Token Standard]. + +The EIP consists of three interfaces which fulfill different roles, found here as {IERC1155}, {IERC1155MetadataURI} and {IERC1155Receiver}. + +{ERC1155} implements the mandatory {IERC1155} interface, as well as the optional extension {IERC1155MetadataURI}, by relying on the substitution mechanism to use the same URI for all token types, dramatically reducing gas costs. + +Additionally there are multiple custom extensions, including: + +* designation of addresses that can pause token transfers for all users ({ERC1155Pausable}). +* destruction of own tokens ({ERC1155Burnable}). + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC1155 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc1155.adoc#Presets[ERC1155 Presets] (such as {ERC1155PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC1155}} + +{{IERC1155MetadataURI}} + +{{ERC1155}} + +{{IERC1155Receiver}} + +{{ERC1155Receiver}} + +== Extensions + +{{ERC1155Pausable}} + +{{ERC1155Burnable}} + +{{ERC1155Supply}} + +{{ERC1155URIStorage}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC1155PresetMinterPauser}} + +== Utilities + +{{ERC1155Holder}} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Burnable.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Burnable.sol new file mode 100644 index 0000000..cc81957 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Burnable.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC1155/extensions/ERC1155Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; + +/** + * @dev Extension of {ERC1155} that allows token holders to destroy both their + * own tokens and those that they have been approved to use. + * + * _Available since v3.1._ + */ +abstract contract ERC1155Burnable is ERC1155 { + function burn(address account, uint256 id, uint256 value) public virtual { + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()), + "ERC1155: caller is not token owner or approved" + ); + + _burn(account, id, value); + } + + function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual { + require( + account == _msgSender() || isApprovedForAll(account, _msgSender()), + "ERC1155: caller is not token owner or approved" + ); + + _burnBatch(account, ids, values); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Pausable.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Pausable.sol new file mode 100644 index 0000000..64790e2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Pausable.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC1155 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + * + * _Available since v3.1._ + */ +abstract contract ERC1155Pausable is ERC1155, Pausable { + /** + * @dev See {ERC1155-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + + require(!paused(), "ERC1155Pausable: token transfer while paused"); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Supply.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Supply.sol new file mode 100644 index 0000000..ec24389 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Supply.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155Supply.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; + +/** + * @dev Extension of ERC1155 that adds tracking of total supply per id. + * + * Useful for scenarios where Fungible and Non-fungible tokens have to be + * clearly identified. Note: While a totalSupply of 1 might mean the + * corresponding is an NFT, there is no guarantees that no other token with the + * same id are not going to be minted. + */ +abstract contract ERC1155Supply is ERC1155 { + mapping(uint256 => uint256) private _totalSupply; + + /** + * @dev Total amount of tokens in with a given id. + */ + function totalSupply(uint256 id) public view virtual returns (uint256) { + return _totalSupply[id]; + } + + /** + * @dev Indicates whether any token exist with a given id, or not. + */ + function exists(uint256 id) public view virtual returns (bool) { + return ERC1155Supply.totalSupply(id) > 0; + } + + /** + * @dev See {ERC1155-_beforeTokenTransfer}. + */ + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + + if (from == address(0)) { + for (uint256 i = 0; i < ids.length; ++i) { + _totalSupply[ids[i]] += amounts[i]; + } + } + + if (to == address(0)) { + for (uint256 i = 0; i < ids.length; ++i) { + uint256 id = ids[i]; + uint256 amount = amounts[i]; + uint256 supply = _totalSupply[id]; + require(supply >= amount, "ERC1155: burn amount exceeds totalSupply"); + unchecked { + _totalSupply[id] = supply - amount; + } + } + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol new file mode 100644 index 0000000..623504f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC1155/extensions/ERC1155URIStorage.sol) + +pragma solidity ^0.8.0; + +import "../../../utils/Strings.sol"; +import "../ERC1155.sol"; + +/** + * @dev ERC1155 token with storage based token URI management. + * Inspired by the ERC721URIStorage extension + * + * _Available since v4.6._ + */ +abstract contract ERC1155URIStorage is ERC1155 { + using Strings for uint256; + + // Optional base URI + string private _baseURI = ""; + + // Optional mapping for token URIs + mapping(uint256 => string) private _tokenURIs; + + /** + * @dev See {IERC1155MetadataURI-uri}. + * + * This implementation returns the concatenation of the `_baseURI` + * and the token-specific uri if the latter is set + * + * This enables the following behaviors: + * + * - if `_tokenURIs[tokenId]` is set, then the result is the concatenation + * of `_baseURI` and `_tokenURIs[tokenId]` (keep in mind that `_baseURI` + * is empty per default); + * + * - if `_tokenURIs[tokenId]` is NOT set then we fallback to `super.uri()` + * which in most cases will contain `ERC1155._uri`; + * + * - if `_tokenURIs[tokenId]` is NOT set, and if the parents do not have a + * uri value set, then the result is empty. + */ + function uri(uint256 tokenId) public view virtual override returns (string memory) { + string memory tokenURI = _tokenURIs[tokenId]; + + // If token URI is set, concatenate base URI and tokenURI (via abi.encodePacked). + return bytes(tokenURI).length > 0 ? string(abi.encodePacked(_baseURI, tokenURI)) : super.uri(tokenId); + } + + /** + * @dev Sets `tokenURI` as the tokenURI of `tokenId`. + */ + function _setURI(uint256 tokenId, string memory tokenURI) internal virtual { + _tokenURIs[tokenId] = tokenURI; + emit URI(uri(tokenId), tokenId); + } + + /** + * @dev Sets `baseURI` as the `_baseURI` for all tokens + */ + function _setBaseURI(string memory baseURI) internal virtual { + _baseURI = baseURI; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol new file mode 100644 index 0000000..520a297 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol) + +pragma solidity ^0.8.0; + +import "../IERC1155.sol"; + +/** + * @dev Interface of the optional ERC1155MetadataExtension interface, as defined + * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. + * + * _Available since v3.1._ + */ +interface IERC1155MetadataURI is IERC1155 { + /** + * @dev Returns the URI for token type `id`. + * + * If the `\{id\}` substring is present in the URI, it must be replaced by + * clients with the actual token type ID. + */ + function uri(uint256 id) external view returns (string memory); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol new file mode 100644 index 0000000..fd7729a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/presets/ERC1155PresetMinterPauser.sol) + +pragma solidity ^0.8.0; + +import "../ERC1155.sol"; +import "../extensions/ERC1155Burnable.sol"; +import "../extensions/ERC1155Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev {ERC1155} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155Burnable, ERC1155Pausable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, and `PAUSER_ROLE` to the account that + * deploys the contract. + */ + constructor(string memory uri) ERC1155(uri) { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + /** + * @dev Creates `amount` new tokens for `to`, of token type `id`. + * + * See {ERC1155-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to, uint256 id, uint256 amount, bytes memory data) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); + + _mint(to, id, amount, data); + } + + /** + * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. + */ + function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); + + _mintBatch(to, ids, amounts, data); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC1155Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC1155Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have pauser role to unpause"); + _unpause(); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(AccessControlEnumerable, ERC1155) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function _beforeTokenTransfer( + address operator, + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal virtual override(ERC1155, ERC1155Pausable) { + super._beforeTokenTransfer(operator, from, to, ids, amounts, data); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/presets/README.md b/lib/openzeppelin-contracts/contracts/token/ERC1155/presets/README.md new file mode 100644 index 0000000..468200b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/presets/README.md @@ -0,0 +1 @@ +Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative. diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol new file mode 100644 index 0000000..7249de8 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol) + +pragma solidity ^0.8.0; + +import "./ERC1155Receiver.sol"; + +/** + * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens. + * + * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be + * stuck. + * + * @dev _Available since v3.1._ + */ +contract ERC1155Holder is ERC1155Receiver { + function onERC1155Received( + address, + address, + uint256, + uint256, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155Received.selector; + } + + function onERC1155BatchReceived( + address, + address, + uint256[] memory, + uint256[] memory, + bytes memory + ) public virtual override returns (bytes4) { + return this.onERC1155BatchReceived.selector; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Receiver.sol b/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Receiver.sol new file mode 100644 index 0000000..2e6804a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Receiver.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol) + +pragma solidity ^0.8.0; + +import "../IERC1155Receiver.sol"; +import "../../../utils/introspection/ERC165.sol"; + +/** + * @dev _Available since v3.1._ + */ +abstract contract ERC1155Receiver is ERC165, IERC1155Receiver { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol new file mode 100644 index 0000000..dfca5a7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) + +pragma solidity ^0.8.0; + +import "./IERC20.sol"; +import "./extensions/IERC20Metadata.sol"; +import "../../utils/Context.sol"; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20, IERC20Metadata { + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + /** + * @dev Sets the values for {name} and {symbol}. + * + * The default value of {decimals} is 18. To select a different value for + * {decimals} you should overload it. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless this function is + * overridden; + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address to, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + * - the caller must have allowance for ``from``'s tokens of at least + * `amount`. + */ + function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, allowance(owner, spender) + addedValue); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + address owner = _msgSender(); + uint256 currentAllowance = allowance(owner, spender); + require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); + unchecked { + _approve(owner, spender, currentAllowance - subtractedValue); + } + + return true; + } + + /** + * @dev Moves `amount` of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `from` must have a balance of at least `amount`. + */ + function _transfer(address from, address to, uint256 amount) internal virtual { + require(from != address(0), "ERC20: transfer from the zero address"); + require(to != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by + // decrementing then incrementing. + _balances[to] += amount; + } + + emit Transfer(from, to, amount); + + _afterTokenTransfer(from, to, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply += amount; + unchecked { + // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. + _balances[account] += amount; + } + emit Transfer(address(0), account, amount); + + _afterTokenTransfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements: + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 accountBalance = _balances[account]; + require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); + unchecked { + _balances[account] = accountBalance - amount; + // Overflow not possible: amount <= accountBalance <= totalSupply. + _totalSupply -= amount; + } + + emit Transfer(account, address(0), amount); + + _afterTokenTransfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} + + /** + * @dev Hook that is called after any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * has been transferred to `to`. + * - when `from` is zero, `amount` tokens have been minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens have been burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol new file mode 100644 index 0000000..66c4e4d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 amount) external returns (bool); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/README.adoc b/lib/openzeppelin-contracts/contracts/token/ERC20/README.adoc new file mode 100644 index 0000000..b3f68e5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/README.adoc @@ -0,0 +1,78 @@ += ERC 20 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc20 + +This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-20[ERC20 Token Standard]. + +TIP: For an overview of ERC20 tokens and a walk through on how to create a token contract read our xref:ROOT:erc20.adoc[ERC20 guide]. + +There are a few core contracts that implement the behavior specified in the EIP: + +* {IERC20}: the interface all ERC20 implementations should conform to. +* {IERC20Metadata}: the extended ERC20 interface including the <>, <> and <> functions. +* {ERC20}: the implementation of the ERC20 interface, including the <>, <> and <> optional standard extension to the base interface. + +Additionally there are multiple custom extensions, including: + +* {ERC20Burnable}: destruction of own tokens. +* {ERC20Capped}: enforcement of a cap to the total supply when minting tokens. +* {ERC20Pausable}: ability to pause token transfers. +* {ERC20Snapshot}: efficient storage of past token balances to be later queried at any point in time. +* {ERC20Permit}: gasless approval of tokens (standardized as ERC2612). +* {ERC20FlashMint}: token level support for flash loans through the minting and burning of ephemeral tokens (standardized as ERC3156). +* {ERC20Votes}: support for voting and vote delegation. +* {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). +* {ERC20Wrapper}: wrapper to create an ERC20 backed by another ERC20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}. +* {ERC4626}: tokenized vault that manages shares (represented as ERC20) that are backed by assets (another ERC20). + +Finally, there are some utilities to interact with ERC20 contracts in various ways. + +* {SafeERC20}: a wrapper around the interface that eliminates the need to handle boolean return values. +* {TokenTimelock}: hold tokens for a beneficiary until a specified time. + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC20 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc20.adoc#Presets[ERC20 Presets] (such as {ERC20PresetMinterPauser}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC20}} + +{{IERC20Metadata}} + +{{ERC20}} + +== Extensions + +{{ERC20Burnable}} + +{{ERC20Capped}} + +{{ERC20Pausable}} + +{{ERC20Permit}} + +{{ERC20Snapshot}} + +{{ERC20Votes}} + +{{ERC20VotesComp}} + +{{ERC20Wrapper}} + +{{ERC20FlashMint}} + +{{ERC4626}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC20PresetMinterPauser}} + +{{ERC20PresetFixedSupply}} + +== Utilities + +{{SafeERC20}} + +{{TokenTimelock}} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol new file mode 100644 index 0000000..1cd08ee --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev Extension of {ERC20} that allows token holders to destroy both their own + * tokens and those that they have an allowance for, in a way that can be + * recognized off-chain (via event analysis). + */ +abstract contract ERC20Burnable is Context, ERC20 { + /** + * @dev Destroys `amount` tokens from the caller. + * + * See {ERC20-_burn}. + */ + function burn(uint256 amount) public virtual { + _burn(_msgSender(), amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, deducting from the caller's + * allowance. + * + * See {ERC20-_burn} and {ERC20-allowance}. + * + * Requirements: + * + * - the caller must have allowance for ``accounts``'s tokens of at least + * `amount`. + */ + function burnFrom(address account, uint256 amount) public virtual { + _spendAllowance(account, _msgSender(), amount); + _burn(account, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Capped.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Capped.sol new file mode 100644 index 0000000..16f830d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Capped.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Capped.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; + +/** + * @dev Extension of {ERC20} that adds a cap to the supply of tokens. + */ +abstract contract ERC20Capped is ERC20 { + uint256 private immutable _cap; + + /** + * @dev Sets the value of the `cap`. This value is immutable, it can only be + * set once during construction. + */ + constructor(uint256 cap_) { + require(cap_ > 0, "ERC20Capped: cap is 0"); + _cap = cap_; + } + + /** + * @dev Returns the cap on the token's total supply. + */ + function cap() public view virtual returns (uint256) { + return _cap; + } + + /** + * @dev See {ERC20-_mint}. + */ + function _mint(address account, uint256 amount) internal virtual override { + require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded"); + super._mint(account, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20FlashMint.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20FlashMint.sol new file mode 100644 index 0000000..063fe99 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20FlashMint.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol) + +pragma solidity ^0.8.0; + +import "../../../interfaces/IERC3156FlashBorrower.sol"; +import "../../../interfaces/IERC3156FlashLender.sol"; +import "../ERC20.sol"; + +/** + * @dev Implementation of the ERC3156 Flash loans extension, as defined in + * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156]. + * + * Adds the {flashLoan} method, which provides flash loan support at the token + * level. By default there is no fee, but this can be changed by overriding {flashFee}. + * + * _Available since v4.1._ + */ +abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender { + bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); + + /** + * @dev Returns the maximum amount of tokens available for loan. + * @param token The address of the token that is requested. + * @return The amount of token that can be loaned. + */ + function maxFlashLoan(address token) public view virtual override returns (uint256) { + return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0; + } + + /** + * @dev Returns the fee applied when doing flash loans. This function calls + * the {_flashFee} function which returns the fee applied when doing flash + * loans. + * @param token The token to be flash loaned. + * @param amount The amount of tokens to be loaned. + * @return The fees applied to the corresponding flash loan. + */ + function flashFee(address token, uint256 amount) public view virtual override returns (uint256) { + require(token == address(this), "ERC20FlashMint: wrong token"); + return _flashFee(token, amount); + } + + /** + * @dev Returns the fee applied when doing flash loans. By default this + * implementation has 0 fees. This function can be overloaded to make + * the flash loan mechanism deflationary. + * @param token The token to be flash loaned. + * @param amount The amount of tokens to be loaned. + * @return The fees applied to the corresponding flash loan. + */ + function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) { + // silence warning about unused variable without the addition of bytecode. + token; + amount; + return 0; + } + + /** + * @dev Returns the receiver address of the flash fee. By default this + * implementation returns the address(0) which means the fee amount will be burnt. + * This function can be overloaded to change the fee receiver. + * @return The address for which the flash fee will be sent to. + */ + function _flashFeeReceiver() internal view virtual returns (address) { + return address(0); + } + + /** + * @dev Performs a flash loan. New tokens are minted and sent to the + * `receiver`, who is required to implement the {IERC3156FlashBorrower} + * interface. By the end of the flash loan, the receiver is expected to own + * amount + fee tokens and have them approved back to the token contract itself so + * they can be burned. + * @param receiver The receiver of the flash loan. Should implement the + * {IERC3156FlashBorrower-onFlashLoan} interface. + * @param token The token to be flash loaned. Only `address(this)` is + * supported. + * @param amount The amount of tokens to be loaned. + * @param data An arbitrary datafield that is passed to the receiver. + * @return `true` if the flash loan was successful. + */ + // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount + // minted at the beginning is always recovered and burned at the end, or else the entire function will revert. + // slither-disable-next-line reentrancy-no-eth + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) public virtual override returns (bool) { + require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan"); + uint256 fee = flashFee(token, amount); + _mint(address(receiver), amount); + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE, + "ERC20FlashMint: invalid return value" + ); + address flashFeeReceiver = _flashFeeReceiver(); + _spendAllowance(address(receiver), address(this), amount + fee); + if (fee == 0 || flashFeeReceiver == address(0)) { + _burn(address(receiver), amount + fee); + } else { + _burn(address(receiver), amount); + _transfer(address(receiver), flashFeeReceiver, fee); + } + return true; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Pausable.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Pausable.sol new file mode 100644 index 0000000..d0b224f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Pausable.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC20 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + */ +abstract contract ERC20Pausable is ERC20, Pausable { + /** + * @dev See {ERC20-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + require(!paused(), "ERC20Pausable: token transfer while paused"); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol new file mode 100644 index 0000000..a357199 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/ERC20Permit.sol) + +pragma solidity ^0.8.0; + +import "./IERC20Permit.sol"; +import "../ERC20.sol"; +import "../../../utils/cryptography/ECDSA.sol"; +import "../../../utils/cryptography/EIP712.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + * + * _Available since v3.4._ + */ +abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { + using Counters for Counters.Counter; + + mapping(address => Counters.Counter) private _nonces; + + // solhint-disable-next-line var-name-mixedcase + bytes32 private constant _PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + /** + * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. + * However, to ensure consistency with the upgradeable transpiler, we will continue + * to reserve a slot. + * @custom:oz-renamed-from _PERMIT_TYPEHASH + */ + // solhint-disable-next-line var-name-mixedcase + bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; + + /** + * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. + * + * It's a good idea to use the same `name` that is defined as the ERC20 token name. + */ + constructor(string memory name) EIP712(name, "1") {} + + /** + * @dev See {IERC20Permit-permit}. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); + + bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); + + bytes32 hash = _hashTypedDataV4(structHash); + + address signer = ECDSA.recover(hash, v, r, s); + require(signer == owner, "ERC20Permit: invalid signature"); + + _approve(owner, spender, value); + } + + /** + * @dev See {IERC20Permit-nonces}. + */ + function nonces(address owner) public view virtual override returns (uint256) { + return _nonces[owner].current(); + } + + /** + * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view override returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @dev "Consume a nonce": return the current value and increment. + * + * _Available since v4.1._ + */ + function _useNonce(address owner) internal virtual returns (uint256 current) { + Counters.Counter storage nonce = _nonces[owner]; + current = nonce.current(); + nonce.increment(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Snapshot.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Snapshot.sol new file mode 100644 index 0000000..ee104b0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Snapshot.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/extensions/ERC20Snapshot.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../../../utils/Arrays.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and + * total supply at the time are recorded for later access. + * + * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. + * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different + * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be + * used to create an efficient ERC20 forking mechanism. + * + * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a + * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot + * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id + * and the account address. + * + * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it + * return `block.number` will trigger the creation of snapshot at the beginning of each new block. When overriding this + * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract. + * + * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient + * alternative consider {ERC20Votes}. + * + * ==== Gas Costs + * + * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log + * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much + * smaller since identical balances in subsequent snapshots are stored as a single entry. + * + * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is + * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent + * transfers will have normal cost until the next snapshot, and so on. + */ + +abstract contract ERC20Snapshot is ERC20 { + // Inspired by Jordi Baylina's MiniMeToken to record historical balances: + // https://github.com/Giveth/minime/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol + + using Arrays for uint256[]; + using Counters for Counters.Counter; + + // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a + // Snapshot struct, but that would impede usage of functions that work on an array. + struct Snapshots { + uint256[] ids; + uint256[] values; + } + + mapping(address => Snapshots) private _accountBalanceSnapshots; + Snapshots private _totalSupplySnapshots; + + // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid. + Counters.Counter private _currentSnapshotId; + + /** + * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created. + */ + event Snapshot(uint256 id); + + /** + * @dev Creates a new snapshot and returns its snapshot id. + * + * Emits a {Snapshot} event that contains the same id. + * + * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a + * set of accounts, for example using {AccessControl}, or it may be open to the public. + * + * [WARNING] + * ==== + * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking, + * you must consider that it can potentially be used by attackers in two ways. + * + * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow + * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target + * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs + * section above. + * + * We haven't measured the actual numbers; if this is something you're interested in please reach out to us. + * ==== + */ + function _snapshot() internal virtual returns (uint256) { + _currentSnapshotId.increment(); + + uint256 currentId = _getCurrentSnapshotId(); + emit Snapshot(currentId); + return currentId; + } + + /** + * @dev Get the current snapshotId + */ + function _getCurrentSnapshotId() internal view virtual returns (uint256) { + return _currentSnapshotId.current(); + } + + /** + * @dev Retrieves the balance of `account` at the time `snapshotId` was created. + */ + function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) { + (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]); + + return snapshotted ? value : balanceOf(account); + } + + /** + * @dev Retrieves the total supply at the time `snapshotId` was created. + */ + function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) { + (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots); + + return snapshotted ? value : totalSupply(); + } + + // Update balance and/or total supply snapshots before the values are modified. This is implemented + // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations. + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { + super._beforeTokenTransfer(from, to, amount); + + if (from == address(0)) { + // mint + _updateAccountSnapshot(to); + _updateTotalSupplySnapshot(); + } else if (to == address(0)) { + // burn + _updateAccountSnapshot(from); + _updateTotalSupplySnapshot(); + } else { + // transfer + _updateAccountSnapshot(from); + _updateAccountSnapshot(to); + } + } + + function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { + require(snapshotId > 0, "ERC20Snapshot: id is 0"); + require(snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id"); + + // When a valid snapshot is queried, there are three possibilities: + // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never + // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds + // to this id is the current one. + // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the + // requested id, and its value is the one to return. + // c) More snapshots were created after the requested one, and the queried value was later modified. There will be + // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is + // larger than the requested one. + // + // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if + // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does + // exactly this. + + uint256 index = snapshots.ids.findUpperBound(snapshotId); + + if (index == snapshots.ids.length) { + return (false, 0); + } else { + return (true, snapshots.values[index]); + } + } + + function _updateAccountSnapshot(address account) private { + _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account)); + } + + function _updateTotalSupplySnapshot() private { + _updateSnapshot(_totalSupplySnapshots, totalSupply()); + } + + function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private { + uint256 currentId = _getCurrentSnapshotId(); + if (_lastSnapshotId(snapshots.ids) < currentId) { + snapshots.ids.push(currentId); + snapshots.values.push(currentValue); + } + } + + function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) { + if (ids.length == 0) { + return 0; + } else { + return ids[ids.length - 1]; + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol new file mode 100644 index 0000000..bf1c2fd --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20Votes.sol) + +pragma solidity ^0.8.0; + +import "./ERC20Permit.sol"; +import "../../../utils/math/Math.sol"; +import "../../../governance/utils/IVotes.sol"; +import "../../../utils/math/SafeCast.sol"; +import "../../../utils/cryptography/ECDSA.sol"; + +/** + * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, + * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1. + * + * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getVotes} and {getPastVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * + * _Available since v4.2._ + */ +abstract contract ERC20Votes is IVotes, ERC20Permit { + struct Checkpoint { + uint32 fromBlock; + uint224 votes; + } + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + mapping(address => address) private _delegates; + mapping(address => Checkpoint[]) private _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) { + return _checkpoints[account][pos]; + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view virtual returns (uint32) { + return SafeCast.toUint32(_checkpoints[account].length); + } + + /** + * @dev Get the address `account` is currently delegating to. + */ + function delegates(address account) public view virtual override returns (address) { + return _delegates[account]; + } + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) public view virtual override returns (uint256) { + uint256 pos = _checkpoints[account].length; + unchecked { + return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; + } + } + + /** + * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) { + require(blockNumber < block.number, "ERC20Votes: block not yet mined"); + return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + } + + /** + * @dev Lookup a value in a list of (sorted) checkpoints. + */ + function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // Initially we check if the block is recent to narrow the search range. + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 length = ckpts.length; + + uint256 low = 0; + uint256 high = length; + + if (length > 5) { + uint256 mid = length - Math.sqrt(length); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + unchecked { + return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + } + } + + /** + * @dev Delegate votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual override { + _delegate(_msgSender(), delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee` + */ + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual override { + require(block.timestamp <= expiry, "ERC20Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ + function _maxSupply() internal view virtual returns (uint224) { + return type(uint224).max; + } + + /** + * @dev Snapshots the totalSupply after it has been increased. + */ + function _mint(address account, uint256 amount) internal virtual override { + super._mint(account, amount); + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + + _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + } + + /** + * @dev Snapshots the totalSupply after it has been decreased. + */ + function _burn(address account, uint256 amount) internal virtual override { + super._burn(account, amount); + + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + } + + /** + * @dev Move voting power when tokens are transferred. + * + * Emits a {IVotes-DelegateVotesChanged} event. + */ + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override { + super._afterTokenTransfer(from, to, amount); + + _moveVotingPower(delegates(from), delegates(to), amount); + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual { + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + _moveVotingPower(currentDelegate, delegatee, delegatorBalance); + } + + function _moveVotingPower(address src, address dst, uint256 amount) private { + if (src != dst && amount > 0) { + if (src != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + + unchecked { + Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1); + + oldWeight = oldCkpt.votes; + newWeight = op(oldWeight, delta); + + if (pos > 0 && oldCkpt.fromBlock == block.number) { + _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push( + Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)}) + ); + } + } + } + + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { + assembly { + mstore(0, ckpts.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20VotesComp.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20VotesComp.sol new file mode 100644 index 0000000..0461310 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20VotesComp.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20VotesComp.sol) + +pragma solidity ^0.8.0; + +import "./ERC20Votes.sol"; + +/** + * @dev Extension of ERC20 to support Compound's voting and delegation. This version exactly matches Compound's + * interface, with the drawback of only supporting supply up to (2^96^ - 1). + * + * NOTE: You should use this contract if you need exact compatibility with COMP (for example in order to use your token + * with Governor Alpha or Bravo) and if you are sure the supply cap of 2^96^ is enough for you. Otherwise, use the + * {ERC20Votes} variant of this module. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getCurrentVotes} and {getPriorVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + * + * _Available since v4.2._ + */ +abstract contract ERC20VotesComp is ERC20Votes { + /** + * @dev Comp version of the {getVotes} accessor, with `uint96` return type. + */ + function getCurrentVotes(address account) external view virtual returns (uint96) { + return SafeCast.toUint96(getVotes(account)); + } + + /** + * @dev Comp version of the {getPastVotes} accessor, with `uint96` return type. + */ + function getPriorVotes(address account, uint256 blockNumber) external view virtual returns (uint96) { + return SafeCast.toUint96(getPastVotes(account, blockNumber)); + } + + /** + * @dev Maximum token supply. Reduced to `type(uint96).max` (2^96^ - 1) to fit COMP interface. + */ + function _maxSupply() internal view virtual override returns (uint224) { + return type(uint96).max; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Wrapper.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Wrapper.sol new file mode 100644 index 0000000..8b153ff --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Wrapper.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/ERC20Wrapper.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../utils/SafeERC20.sol"; + +/** + * @dev Extension of the ERC20 token contract to support token wrapping. + * + * Users can deposit and withdraw "underlying tokens" and receive a matching number of "wrapped tokens". This is useful + * in conjunction with other modules. For example, combining this wrapping mechanism with {ERC20Votes} will allow the + * wrapping of an existing "basic" ERC20 into a governance token. + * + * _Available since v4.2._ + */ +abstract contract ERC20Wrapper is ERC20 { + IERC20 public immutable underlying; + + constructor(IERC20 underlyingToken) { + underlying = underlyingToken; + } + + /** + * @dev See {ERC20-decimals}. + */ + function decimals() public view virtual override returns (uint8) { + try IERC20Metadata(address(underlying)).decimals() returns (uint8 value) { + return value; + } catch { + return super.decimals(); + } + } + + /** + * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. + */ + function depositFor(address account, uint256 amount) public virtual returns (bool) { + SafeERC20.safeTransferFrom(underlying, _msgSender(), address(this), amount); + _mint(account, amount); + return true; + } + + /** + * @dev Allow a user to burn a number of wrapped tokens and withdraw the corresponding number of underlying tokens. + */ + function withdrawTo(address account, uint256 amount) public virtual returns (bool) { + _burn(_msgSender(), amount); + SafeERC20.safeTransfer(underlying, account, amount); + return true; + } + + /** + * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal + * function that can be exposed with access control if desired. + */ + function _recover(address account) internal virtual returns (uint256) { + uint256 value = underlying.balanceOf(address(this)) - totalSupply(); + _mint(account, value); + return value; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol new file mode 100644 index 0000000..9b26aef --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC4626.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../utils/SafeERC20.sol"; +import "../../../interfaces/IERC4626.sol"; +import "../../../utils/math/Math.sol"; + +/** + * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in + * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. + * + * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for + * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends + * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this + * contract and not the "assets" token which is an independent contract. + * + * CAUTION: When the vault is empty or nearly empty, deposits are at high risk of being stolen through frontrunning with + * a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation + * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial + * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may + * similarly be affected by slippage. Users can protect against this attack as well unexpected slippage in general by + * verifying the amount received is as expected, using a wrapper that performs these checks such as + * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. + * + * _Available since v4.7._ + */ +abstract contract ERC4626 is ERC20, IERC4626 { + using Math for uint256; + + IERC20 private immutable _asset; + uint8 private immutable _decimals; + + /** + * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). + */ + constructor(IERC20 asset_) { + (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); + _decimals = success ? assetDecimals : super.decimals(); + _asset = asset_; + } + + /** + * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. + */ + function _tryGetAssetDecimals(IERC20 asset_) private returns (bool, uint8) { + (bool success, bytes memory encodedDecimals) = address(asset_).call( + abi.encodeWithSelector(IERC20Metadata.decimals.selector) + ); + if (success && encodedDecimals.length >= 32) { + uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); + if (returnedDecimals <= type(uint8).max) { + return (true, uint8(returnedDecimals)); + } + } + return (false, 0); + } + + /** + * @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset + * has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on + * inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value. + * See {IERC20Metadata-decimals}. + */ + function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) { + return _decimals; + } + + /** @dev See {IERC4626-asset}. */ + function asset() public view virtual override returns (address) { + return address(_asset); + } + + /** @dev See {IERC4626-totalAssets}. */ + function totalAssets() public view virtual override returns (uint256) { + return _asset.balanceOf(address(this)); + } + + /** @dev See {IERC4626-convertToShares}. */ + function convertToShares(uint256 assets) public view virtual override returns (uint256) { + return _convertToShares(assets, Math.Rounding.Down); + } + + /** @dev See {IERC4626-convertToAssets}. */ + function convertToAssets(uint256 shares) public view virtual override returns (uint256) { + return _convertToAssets(shares, Math.Rounding.Down); + } + + /** @dev See {IERC4626-maxDeposit}. */ + function maxDeposit(address) public view virtual override returns (uint256) { + return _isVaultHealthy() ? type(uint256).max : 0; + } + + /** @dev See {IERC4626-maxMint}. */ + function maxMint(address) public view virtual override returns (uint256) { + return type(uint256).max; + } + + /** @dev See {IERC4626-maxWithdraw}. */ + function maxWithdraw(address owner) public view virtual override returns (uint256) { + return _convertToAssets(balanceOf(owner), Math.Rounding.Down); + } + + /** @dev See {IERC4626-maxRedeem}. */ + function maxRedeem(address owner) public view virtual override returns (uint256) { + return balanceOf(owner); + } + + /** @dev See {IERC4626-previewDeposit}. */ + function previewDeposit(uint256 assets) public view virtual override returns (uint256) { + return _convertToShares(assets, Math.Rounding.Down); + } + + /** @dev See {IERC4626-previewMint}. */ + function previewMint(uint256 shares) public view virtual override returns (uint256) { + return _convertToAssets(shares, Math.Rounding.Up); + } + + /** @dev See {IERC4626-previewWithdraw}. */ + function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { + return _convertToShares(assets, Math.Rounding.Up); + } + + /** @dev See {IERC4626-previewRedeem}. */ + function previewRedeem(uint256 shares) public view virtual override returns (uint256) { + return _convertToAssets(shares, Math.Rounding.Down); + } + + /** @dev See {IERC4626-deposit}. */ + function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { + require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max"); + + uint256 shares = previewDeposit(assets); + _deposit(_msgSender(), receiver, assets, shares); + + return shares; + } + + /** @dev See {IERC4626-mint}. + * + * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. + * In this case, the shares will be minted without requiring any assets to be deposited. + */ + function mint(uint256 shares, address receiver) public virtual override returns (uint256) { + require(shares <= maxMint(receiver), "ERC4626: mint more than max"); + + uint256 assets = previewMint(shares); + _deposit(_msgSender(), receiver, assets, shares); + + return assets; + } + + /** @dev See {IERC4626-withdraw}. */ + function withdraw(uint256 assets, address receiver, address owner) public virtual override returns (uint256) { + require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max"); + + uint256 shares = previewWithdraw(assets); + _withdraw(_msgSender(), receiver, owner, assets, shares); + + return shares; + } + + /** @dev See {IERC4626-redeem}. */ + function redeem(uint256 shares, address receiver, address owner) public virtual override returns (uint256) { + require(shares <= maxRedeem(owner), "ERC4626: redeem more than max"); + + uint256 assets = previewRedeem(shares); + _withdraw(_msgSender(), receiver, owner, assets, shares); + + return assets; + } + + /** + * @dev Internal conversion function (from assets to shares) with support for rounding direction. + * + * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset + * would represent an infinite amount of shares. + */ + function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) { + uint256 supply = totalSupply(); + return + (assets == 0 || supply == 0) + ? _initialConvertToShares(assets, rounding) + : assets.mulDiv(supply, totalAssets(), rounding); + } + + /** + * @dev Internal conversion function (from assets to shares) to apply when the vault is empty. + * + * NOTE: Make sure to keep this function consistent with {_initialConvertToAssets} when overriding it. + */ + function _initialConvertToShares( + uint256 assets, + Math.Rounding /*rounding*/ + ) internal view virtual returns (uint256 shares) { + return assets; + } + + /** + * @dev Internal conversion function (from shares to assets) with support for rounding direction. + */ + function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) { + uint256 supply = totalSupply(); + return + (supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding); + } + + /** + * @dev Internal conversion function (from shares to assets) to apply when the vault is empty. + * + * NOTE: Make sure to keep this function consistent with {_initialConvertToShares} when overriding it. + */ + function _initialConvertToAssets( + uint256 shares, + Math.Rounding /*rounding*/ + ) internal view virtual returns (uint256) { + return shares; + } + + /** + * @dev Deposit/mint common workflow. + */ + function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { + // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the + // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, + // calls the vault, which is assumed not malicious. + // + // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the + // assets are transferred and before the shares are minted, which is a valid state. + // slither-disable-next-line reentrancy-no-eth + SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); + _mint(receiver, shares); + + emit Deposit(caller, receiver, assets, shares); + } + + /** + * @dev Withdraw/redeem common workflow. + */ + function _withdraw( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares + ) internal virtual { + if (caller != owner) { + _spendAllowance(owner, caller, shares); + } + + // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the + // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, + // calls the vault, which is assumed not malicious. + // + // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the + // shares are burned and after the assets are transferred, which is a valid state. + _burn(owner, shares); + SafeERC20.safeTransfer(_asset, receiver, assets); + + emit Withdraw(caller, receiver, owner, assets, shares); + } + + /** + * @dev Checks if vault is "healthy" in the sense of having assets backing the circulating shares. + */ + function _isVaultHealthy() private view returns (bool) { + return totalAssets() > 0 || totalSupply() == 0; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol new file mode 100644 index 0000000..83ba6ac --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol new file mode 100644 index 0000000..bb43e53 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol new file mode 100644 index 0000000..6579ef3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol) + +pragma solidity ^0.8.0; + +// EIP-2612 is Final as of 2022-11-01. This file is deprecated. + +import "./ERC20Permit.sol"; diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol new file mode 100644 index 0000000..1df6c53 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +// EIP-2612 is Final as of 2022-11-01. This file is deprecated. + +import "./IERC20Permit.sol"; diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol new file mode 100644 index 0000000..e826814 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetFixedSupply.sol) +pragma solidity ^0.8.0; + +import "../extensions/ERC20Burnable.sol"; + +/** + * @dev {ERC20} token, including: + * + * - Preminted initial supply + * - Ability for holders to burn (destroy) their tokens + * - No access control mechanism (for minting/pausing) and hence no governance + * + * This contract uses {ERC20Burnable} to include burn capabilities - head to + * its documentation for details. + * + * _Available since v3.4._ + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC20PresetFixedSupply is ERC20Burnable { + /** + * @dev Mints `initialSupply` amount of token and transfers them to `owner`. + * + * See {ERC20-constructor}. + */ + constructor(string memory name, string memory symbol, uint256 initialSupply, address owner) ERC20(name, symbol) { + _mint(owner, initialSupply); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol new file mode 100644 index 0000000..e711a89 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/presets/ERC20PresetMinterPauser.sol) + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../extensions/ERC20Burnable.sol"; +import "../extensions/ERC20Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; + +/** + * @dev {ERC20} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the + * account that deploys the contract. + * + * See {ERC20-constructor}. + */ + constructor(string memory name, string memory symbol) ERC20(name, symbol) { + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + /** + * @dev Creates `amount` new tokens for `to`. + * + * See {ERC20-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to, uint256 amount) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint"); + _mint(to, amount); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC20Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC20Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause"); + _unpause(); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override(ERC20, ERC20Pausable) { + super._beforeTokenTransfer(from, to, amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/presets/README.md b/lib/openzeppelin-contracts/contracts/token/ERC20/presets/README.md new file mode 100644 index 0000000..468200b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/presets/README.md @@ -0,0 +1 @@ +Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative. diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol new file mode 100644 index 0000000..028711d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; +import "../extensions/IERC20Permit.sol"; +import "../../../utils/Address.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove(IERC20 token, address spender, uint256 value) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender) + value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { + unchecked { + uint256 oldAllowance = token.allowance(address(this), spender); + require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); + uint256 newAllowance = oldAllowance - value; + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + } + + function safePermit( + IERC20Permit token, + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + uint256 nonceBefore = token.nonces(owner); + token.permit(owner, spender, value, deadline, v, r, s); + uint256 nonceAfter = token.nonces(owner); + require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { + // Return data is optional + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC20/utils/TokenTimelock.sol b/lib/openzeppelin-contracts/contracts/token/ERC20/utils/TokenTimelock.sol new file mode 100644 index 0000000..ed855b7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC20/utils/TokenTimelock.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/utils/TokenTimelock.sol) + +pragma solidity ^0.8.0; + +import "./SafeERC20.sol"; + +/** + * @dev A token holder contract that will allow a beneficiary to extract the + * tokens after a given release time. + * + * Useful for simple vesting schedules like "advisors get all of their tokens + * after 1 year". + */ +contract TokenTimelock { + using SafeERC20 for IERC20; + + // ERC20 basic token contract being held + IERC20 private immutable _token; + + // beneficiary of tokens after they are released + address private immutable _beneficiary; + + // timestamp when token release is enabled + uint256 private immutable _releaseTime; + + /** + * @dev Deploys a timelock instance that is able to hold the token specified, and will only release it to + * `beneficiary_` when {release} is invoked after `releaseTime_`. The release time is specified as a Unix timestamp + * (in seconds). + */ + constructor(IERC20 token_, address beneficiary_, uint256 releaseTime_) { + require(releaseTime_ > block.timestamp, "TokenTimelock: release time is before current time"); + _token = token_; + _beneficiary = beneficiary_; + _releaseTime = releaseTime_; + } + + /** + * @dev Returns the token being held. + */ + function token() public view virtual returns (IERC20) { + return _token; + } + + /** + * @dev Returns the beneficiary that will receive the tokens. + */ + function beneficiary() public view virtual returns (address) { + return _beneficiary; + } + + /** + * @dev Returns the time when the tokens are released in seconds since Unix epoch (i.e. Unix timestamp). + */ + function releaseTime() public view virtual returns (uint256) { + return _releaseTime; + } + + /** + * @dev Transfers tokens held by the timelock to the beneficiary. Will only succeed if invoked after the release + * time. + */ + function release() public virtual { + require(block.timestamp >= releaseTime(), "TokenTimelock: current time is before release time"); + + uint256 amount = token().balanceOf(address(this)); + require(amount > 0, "TokenTimelock: no tokens to release"); + + token().safeTransfer(beneficiary(), amount); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol new file mode 100644 index 0000000..6bf620b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.0; + +import "./IERC721.sol"; +import "./IERC721Receiver.sol"; +import "./extensions/IERC721Metadata.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/Strings.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including + * the Metadata extension, but not including the Enumerable extension, which is available separately as + * {ERC721Enumerable}. + */ +contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { + using Address for address; + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + // Mapping from token ID to owner address + mapping(uint256 => address) private _owners; + + // Mapping owner address to token count + mapping(address => uint256) private _balances; + + // Mapping from token ID to approved address + mapping(uint256 => address) private _tokenApprovals; + + // Mapping from owner to operator approvals + mapping(address => mapping(address => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721-balanceOf}. + */ + function balanceOf(address owner) public view virtual override returns (uint256) { + require(owner != address(0), "ERC721: address zero is not a valid owner"); + return _balances[owner]; + } + + /** + * @dev See {IERC721-ownerOf}. + */ + function ownerOf(uint256 tokenId) public view virtual override returns (address) { + address owner = _ownerOf(tokenId); + require(owner != address(0), "ERC721: invalid token ID"); + return owner; + } + + /** + * @dev See {IERC721Metadata-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC721Metadata-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + _requireMinted(tokenId); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overridden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /** + * @dev See {IERC721-approve}. + */ + function approve(address to, uint256 tokenId) public virtual override { + address owner = ERC721.ownerOf(tokenId); + require(to != owner, "ERC721: approval to current owner"); + + require( + _msgSender() == owner || isApprovedForAll(owner, _msgSender()), + "ERC721: approve caller is not token owner or approved for all" + ); + + _approve(to, tokenId); + } + + /** + * @dev See {IERC721-getApproved}. + */ + function getApproved(uint256 tokenId) public view virtual override returns (address) { + _requireMinted(tokenId); + + return _tokenApprovals[tokenId]; + } + + /** + * @dev See {IERC721-setApprovalForAll}. + */ + function setApprovalForAll(address operator, bool approved) public virtual override { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /** + * @dev See {IERC721-isApprovedForAll}. + */ + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { + return _operatorApprovals[owner][operator]; + } + + /** + * @dev See {IERC721-transferFrom}. + */ + function transferFrom(address from, address to, uint256 tokenId) public virtual override { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); + + _transfer(from, to, tokenId); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { + safeTransferFrom(from, to, tokenId, ""); + } + + /** + * @dev See {IERC721-safeTransferFrom}. + */ + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); + _safeTransfer(from, to, tokenId, data); + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * `data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { + _transfer(from, to, tokenId); + require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer"); + } + + /** + * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist + */ + function _ownerOf(uint256 tokenId) internal view virtual returns (address) { + return _owners[tokenId]; + } + + /** + * @dev Returns whether `tokenId` exists. + * + * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. + * + * Tokens start existing when they are minted (`_mint`), + * and stop existing when they are burned (`_burn`). + */ + function _exists(uint256 tokenId) internal view virtual returns (bool) { + return _ownerOf(tokenId) != address(0); + } + + /** + * @dev Returns whether `spender` is allowed to manage `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { + address owner = ERC721.ownerOf(tokenId); + return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); + } + + /** + * @dev Safely mints `tokenId` and transfers it to `to`. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal virtual { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { + _mint(to, tokenId); + require( + _checkOnERC721Received(address(0), to, tokenId, data), + "ERC721: transfer to non ERC721Receiver implementer" + ); + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal virtual { + require(to != address(0), "ERC721: mint to the zero address"); + require(!_exists(tokenId), "ERC721: token already minted"); + + _beforeTokenTransfer(address(0), to, tokenId, 1); + + // Check that tokenId was not minted by `_beforeTokenTransfer` hook + require(!_exists(tokenId), "ERC721: token already minted"); + + unchecked { + // Will not overflow unless all 2**256 token ids are minted to the same owner. + // Given that tokens are minted one by one, it is impossible in practice that + // this ever happens. Might change if we allow batch minting. + // The ERC fails to describe this case. + _balances[to] += 1; + } + + _owners[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + + _afterTokenTransfer(address(0), to, tokenId, 1); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * This is an internal function that does not check if the sender is authorized to operate on the token. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal virtual { + address owner = ERC721.ownerOf(tokenId); + + _beforeTokenTransfer(owner, address(0), tokenId, 1); + + // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook + owner = ERC721.ownerOf(tokenId); + + // Clear approvals + delete _tokenApprovals[tokenId]; + + unchecked { + // Cannot overflow, as that would require more tokens to be burned/transferred + // out than the owner initially received through minting and transferring in. + _balances[owner] -= 1; + } + delete _owners[tokenId]; + + emit Transfer(owner, address(0), tokenId); + + _afterTokenTransfer(owner, address(0), tokenId, 1); + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer(address from, address to, uint256 tokenId) internal virtual { + require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); + require(to != address(0), "ERC721: transfer to the zero address"); + + _beforeTokenTransfer(from, to, tokenId, 1); + + // Check that tokenId was not transferred by `_beforeTokenTransfer` hook + require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); + + // Clear approvals from the previous owner + delete _tokenApprovals[tokenId]; + + unchecked { + // `_balances[from]` cannot overflow for the same reason as described in `_burn`: + // `from`'s balance is the number of token held, which is at least one before the current + // transfer. + // `_balances[to]` could overflow in the conditions described in `_mint`. That would require + // all 2**256 token ids to be minted, which in practice is impossible. + _balances[from] -= 1; + _balances[to] += 1; + } + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + + _afterTokenTransfer(from, to, tokenId, 1); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * Emits an {Approval} event. + */ + function _approve(address to, uint256 tokenId) internal virtual { + _tokenApprovals[tokenId] = to; + emit Approval(ERC721.ownerOf(tokenId), to, tokenId); + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Emits an {ApprovalForAll} event. + */ + function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { + require(owner != operator, "ERC721: approve to caller"); + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Reverts if the `tokenId` has not been minted yet. + */ + function _requireMinted(uint256 tokenId) internal view virtual { + require(_exists(tokenId), "ERC721: invalid token ID"); + } + + /** + * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. + * The call is not executed if the target address is not a contract. + * + * @param from address representing the previous owner of the given token ID + * @param to target address that will receive the tokens + * @param tokenId uint256 ID of the token to be transferred + * @param data bytes optional data to send along with the call + * @return bool whether the call correctly returned the expected magic value + */ + function _checkOnERC721Received( + address from, + address to, + uint256 tokenId, + bytes memory data + ) private returns (bool) { + if (to.isContract()) { + try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { + return retval == IERC721Receiver.onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC721: transfer to non ERC721Receiver implementer"); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } else { + return true; + } + } + + /** + * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is + * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`. + * - When `from` is zero, the tokens will be minted for `to`. + * - When `to` is zero, ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * - `batchSize` is non-zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 /* firstTokenId */, + uint256 batchSize + ) internal virtual { + if (batchSize > 1) { + if (from != address(0)) { + _balances[from] -= batchSize; + } + if (to != address(0)) { + _balances[to] += batchSize; + } + } + } + + /** + * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is + * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1. + * + * Calling conditions: + * + * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`. + * - When `from` is zero, the tokens were minted for `to`. + * - When `to` is zero, ``from``'s tokens were burned. + * - `from` and `to` are never both zero. + * - `batchSize` is non-zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {} +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol new file mode 100644 index 0000000..646530a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol) + +pragma solidity ^0.8.0; + +import "../../utils/introspection/IERC165.sol"; + +/** + * @dev Required interface of an ERC721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 + * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must + * understand this adds an external call which potentially creates a reentrancy vulnerability. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool _approved) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol new file mode 100644 index 0000000..de67209 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) + +pragma solidity ^0.8.0; + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. + * + * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/README.adoc b/lib/openzeppelin-contracts/contracts/token/ERC721/README.adoc new file mode 100644 index 0000000..b3377af --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/README.adoc @@ -0,0 +1,70 @@ += ERC 721 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc721 + +This set of interfaces, contracts, and utilities are all related to the https://eips.ethereum.org/EIPS/eip-721[ERC721 Non-Fungible Token Standard]. + +TIP: For a walk through on how to create an ERC721 token read our xref:ROOT:erc721.adoc[ERC721 guide]. + +The EIP specifies four interfaces: + +* {IERC721}: Core functionality required in all compliant implementation. +* {IERC721Metadata}: Optional extension that adds name, symbol, and token URI, almost always included. +* {IERC721Enumerable}: Optional extension that allows enumerating the tokens on chain, often not included since it requires large gas overhead. +* {IERC721Receiver}: An interface that must be implemented by contracts if they want to accept tokens through `safeTransferFrom`. + +OpenZeppelin Contracts provides implementations of all four interfaces: + +* {ERC721}: The core and metadata extensions, with a base URI mechanism. +* {ERC721Enumerable}: The enumerable extension. +* {ERC721Holder}: A bare bones implementation of the receiver interface. + +Additionally there are a few of other extensions: + +* {ERC721Consecutive}: An implementation of https://eips.ethereum.org/EIPS/eip-2309[ERC2309] for minting batchs of tokens during construction, in accordance with ERC721. +* {ERC721URIStorage}: A more flexible but more expensive way of storing metadata. +* {ERC721Votes}: Support for voting and vote delegation. +* {ERC721Royalty}: A way to signal royalty information following ERC2981. +* {ERC721Pausable}: A primitive to pause contract operation. +* {ERC721Burnable}: A way for token holders to burn their own tokens. + +NOTE: This core set of contracts is designed to be unopinionated, allowing developers to access the internal functions in ERC721 (such as <>) and expose them as external functions in the way they prefer. On the other hand, xref:ROOT:erc721.adoc#Presets[ERC721 Presets] (such as {ERC721PresetMinterPauserAutoId}) are designed using opinionated patterns to provide developers with ready to use, deployable contracts. + +== Core + +{{IERC721}} + +{{IERC721Metadata}} + +{{IERC721Enumerable}} + +{{ERC721}} + +{{ERC721Enumerable}} + +{{IERC721Receiver}} + +== Extensions + +{{ERC721Pausable}} + +{{ERC721Burnable}} + +{{ERC721Consecutive}} + +{{ERC721URIStorage}} + +{{ERC721Votes}} + +{{ERC721Royalty}} + +== Presets + +These contracts are preconfigured combinations of the above features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC721PresetMinterPauserAutoId}} + +== Utilities + +{{ERC721Holder}} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Burnable.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Burnable.sol new file mode 100644 index 0000000..0dc7dae --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Burnable.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../utils/Context.sol"; + +/** + * @title ERC721 Burnable Token + * @dev ERC721 Token that can be burned (destroyed). + */ +abstract contract ERC721Burnable is Context, ERC721 { + /** + * @dev Burns `tokenId`. See {ERC721-_burn}. + * + * Requirements: + * + * - The caller must own `tokenId` or be an approved operator. + */ + function burn(uint256 tokenId) public virtual { + //solhint-disable-next-line max-line-length + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved"); + _burn(tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Consecutive.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Consecutive.sol new file mode 100644 index 0000000..e6843f1 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Consecutive.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Consecutive.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../interfaces/IERC2309.sol"; +import "../../../utils/Checkpoints.sol"; +import "../../../utils/structs/BitMaps.sol"; + +/** + * @dev Implementation of the ERC2309 "Consecutive Transfer Extension" as defined in + * https://eips.ethereum.org/EIPS/eip-2309[EIP-2309]. + * + * This extension allows the minting of large batches of tokens, during contract construction only. For upgradeable + * contracts this implies that batch minting is only available during proxy deployment, and not in subsequent upgrades. + * These batches are limited to 5000 tokens at a time by default to accommodate off-chain indexers. + * + * Using this extension removes the ability to mint single tokens during contract construction. This ability is + * regained after construction. During construction, only batch minting is allowed. + * + * IMPORTANT: This extension bypasses the hooks {_beforeTokenTransfer} and {_afterTokenTransfer} for tokens minted in + * batch. When using this extension, you should consider the {_beforeConsecutiveTokenTransfer} and + * {_afterConsecutiveTokenTransfer} hooks in addition to {_beforeTokenTransfer} and {_afterTokenTransfer}. + * + * IMPORTANT: When overriding {_afterTokenTransfer}, be careful about call ordering. {ownerOf} may return invalid + * values during the {_afterTokenTransfer} execution if the super call is not called first. To be safe, execute the + * super call before your custom logic. + * + * _Available since v4.8._ + */ +abstract contract ERC721Consecutive is IERC2309, ERC721 { + using BitMaps for BitMaps.BitMap; + using Checkpoints for Checkpoints.Trace160; + + Checkpoints.Trace160 private _sequentialOwnership; + BitMaps.BitMap private _sequentialBurn; + + /** + * @dev Maximum size of a batch of consecutive tokens. This is designed to limit stress on off-chain indexing + * services that have to record one entry per token, and have protections against "unreasonably large" batches of + * tokens. + * + * NOTE: Overriding the default value of 5000 will not cause on-chain issues, but may result in the asset not being + * correctly supported by off-chain indexing services (including marketplaces). + */ + function _maxBatchSize() internal view virtual returns (uint96) { + return 5000; + } + + /** + * @dev See {ERC721-_ownerOf}. Override that checks the sequential ownership structure for tokens that have + * been minted as part of a batch, and not yet transferred. + */ + function _ownerOf(uint256 tokenId) internal view virtual override returns (address) { + address owner = super._ownerOf(tokenId); + + // If token is owned by the core, or beyond consecutive range, return base value + if (owner != address(0) || tokenId > type(uint96).max) { + return owner; + } + + // Otherwise, check the token was not burned, and fetch ownership from the anchors + // Note: no need for safe cast, we know that tokenId <= type(uint96).max + return _sequentialBurn.get(tokenId) ? address(0) : address(_sequentialOwnership.lowerLookup(uint96(tokenId))); + } + + /** + * @dev Mint a batch of tokens of length `batchSize` for `to`. Returns the token id of the first token minted in the + * batch; if `batchSize` is 0, returns the number of consecutive ids minted so far. + * + * Requirements: + * + * - `batchSize` must not be greater than {_maxBatchSize}. + * - The function is called in the constructor of the contract (directly or indirectly). + * + * CAUTION: Does not emit a `Transfer` event. This is ERC721 compliant as long as it is done inside of the + * constructor, which is enforced by this function. + * + * CAUTION: Does not invoke `onERC721Received` on the receiver. + * + * Emits a {IERC2309-ConsecutiveTransfer} event. + */ + function _mintConsecutive(address to, uint96 batchSize) internal virtual returns (uint96) { + uint96 first = _totalConsecutiveSupply(); + + // minting a batch of size 0 is a no-op + if (batchSize > 0) { + require(!Address.isContract(address(this)), "ERC721Consecutive: batch minting restricted to constructor"); + require(to != address(0), "ERC721Consecutive: mint to the zero address"); + require(batchSize <= _maxBatchSize(), "ERC721Consecutive: batch too large"); + + // hook before + _beforeTokenTransfer(address(0), to, first, batchSize); + + // push an ownership checkpoint & emit event + uint96 last = first + batchSize - 1; + _sequentialOwnership.push(last, uint160(to)); + emit ConsecutiveTransfer(first, last, address(0), to); + + // hook after + _afterTokenTransfer(address(0), to, first, batchSize); + } + + return first; + } + + /** + * @dev See {ERC721-_mint}. Override version that restricts normal minting to after construction. + * + * Warning: Using {ERC721Consecutive} prevents using {_mint} during construction in favor of {_mintConsecutive}. + * After construction, {_mintConsecutive} is no longer available and {_mint} becomes available. + */ + function _mint(address to, uint256 tokenId) internal virtual override { + require(Address.isContract(address(this)), "ERC721Consecutive: can't mint during construction"); + super._mint(to, tokenId); + } + + /** + * @dev See {ERC721-_afterTokenTransfer}. Burning of tokens that have been sequentially minted must be explicit. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + if ( + to == address(0) && // if we burn + firstTokenId < _totalConsecutiveSupply() && // and the tokenId was minted in a batch + !_sequentialBurn.get(firstTokenId) // and the token was never marked as burnt + ) { + require(batchSize == 1, "ERC721Consecutive: batch burn not supported"); + _sequentialBurn.set(firstTokenId); + } + super._afterTokenTransfer(from, to, firstTokenId, batchSize); + } + + function _totalConsecutiveSupply() private view returns (uint96) { + (bool exists, uint96 latestId, ) = _sequentialOwnership.latestCheckpoint(); + return exists ? latestId + 1 : 0; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sol new file mode 100644 index 0000000..aab81a9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "./IERC721Enumerable.sol"; + +/** + * @dev This implements an optional extension of {ERC721} defined in the EIP that adds + * enumerability of all the token ids in the contract as well as all token ids owned by each + * account. + */ +abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { + // Mapping from owner to list of owned token IDs + mapping(address => mapping(uint256 => uint256)) private _ownedTokens; + + // Mapping from token ID to index of the owner tokens list + mapping(uint256 => uint256) private _ownedTokensIndex; + + // Array with all token ids, used for enumeration + uint256[] private _allTokens; + + // Mapping from token id to position in the allTokens array + mapping(uint256 => uint256) private _allTokensIndex; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { + require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds"); + return _ownedTokens[owner][index]; + } + + /** + * @dev See {IERC721Enumerable-totalSupply}. + */ + function totalSupply() public view virtual override returns (uint256) { + return _allTokens.length; + } + + /** + * @dev See {IERC721Enumerable-tokenByIndex}. + */ + function tokenByIndex(uint256 index) public view virtual override returns (uint256) { + require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds"); + return _allTokens[index]; + } + + /** + * @dev See {ERC721-_beforeTokenTransfer}. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + + if (batchSize > 1) { + // Will only trigger during construction. Batch transferring (minting) is not available afterwards. + revert("ERC721Enumerable: consecutive transfers not supported"); + } + + uint256 tokenId = firstTokenId; + + if (from == address(0)) { + _addTokenToAllTokensEnumeration(tokenId); + } else if (from != to) { + _removeTokenFromOwnerEnumeration(from, tokenId); + } + if (to == address(0)) { + _removeTokenFromAllTokensEnumeration(tokenId); + } else if (to != from) { + _addTokenToOwnerEnumeration(to, tokenId); + } + } + + /** + * @dev Private function to add a token to this extension's ownership-tracking data structures. + * @param to address representing the new owner of the given token ID + * @param tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { + uint256 length = ERC721.balanceOf(to); + _ownedTokens[to][length] = tokenId; + _ownedTokensIndex[tokenId] = length; + } + + /** + * @dev Private function to add a token to this extension's token tracking data structures. + * @param tokenId uint256 ID of the token to be added to the tokens list + */ + function _addTokenToAllTokensEnumeration(uint256 tokenId) private { + _allTokensIndex[tokenId] = _allTokens.length; + _allTokens.push(tokenId); + } + + /** + * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that + * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for + * gas optimizations e.g. when performing a transfer operation (avoiding double writes). + * This has O(1) time complexity, but alters the order of the _ownedTokens array. + * @param from address representing the previous owner of the given token ID + * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = ERC721.balanceOf(from) - 1; + uint256 tokenIndex = _ownedTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary + if (tokenIndex != lastTokenIndex) { + uint256 lastTokenId = _ownedTokens[from][lastTokenIndex]; + + _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + } + + // This also deletes the contents at the last position of the array + delete _ownedTokensIndex[tokenId]; + delete _ownedTokens[from][lastTokenIndex]; + } + + /** + * @dev Private function to remove a token from this extension's token tracking data structures. + * This has O(1) time complexity, but alters the order of the _allTokens array. + * @param tokenId uint256 ID of the token to be removed from the tokens list + */ + function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { + // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = _allTokens.length - 1; + uint256 tokenIndex = _allTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so + // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding + // an 'if' statement (like in _removeTokenFromOwnerEnumeration) + uint256 lastTokenId = _allTokens[lastTokenIndex]; + + _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + + // This also deletes the contents at the last position of the array + delete _allTokensIndex[tokenId]; + _allTokens.pop(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pausable.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pausable.sol new file mode 100644 index 0000000..4726540 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pausable.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Pausable.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../security/Pausable.sol"; + +/** + * @dev ERC721 token with pausable token transfers, minting and burning. + * + * Useful for scenarios such as preventing trades until the end of an evaluation + * period, or having an emergency switch for freezing all token transfers in the + * event of a large bug. + */ +abstract contract ERC721Pausable is ERC721, Pausable { + /** + * @dev See {ERC721-_beforeTokenTransfer}. + * + * Requirements: + * + * - the contract must not be paused. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + + require(!paused(), "ERC721Pausable: token transfer while paused"); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Royalty.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Royalty.sol new file mode 100644 index 0000000..298e342 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Royalty.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Royalty.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../common/ERC2981.sol"; +import "../../../utils/introspection/ERC165.sol"; + +/** + * @dev Extension of ERC721 with the ERC2981 NFT Royalty Standard, a standardized way to retrieve royalty payment + * information. + * + * Royalty information can be specified globally for all token ids via {ERC2981-_setDefaultRoyalty}, and/or individually for + * specific token ids via {ERC2981-_setTokenRoyalty}. The latter takes precedence over the first. + * + * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See + * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to + * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported. + * + * _Available since v4.5._ + */ +abstract contract ERC721Royalty is ERC2981, ERC721 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC2981) returns (bool) { + return super.supportsInterface(interfaceId); + } + + /** + * @dev See {ERC721-_burn}. This override additionally clears the royalty information for the token. + */ + function _burn(uint256 tokenId) internal virtual override { + super._burn(tokenId); + _resetTokenRoyalty(tokenId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol new file mode 100644 index 0000000..e83a5ed --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; + +/** + * @dev ERC721 token with storage based token URI management. + */ +abstract contract ERC721URIStorage is ERC721 { + using Strings for uint256; + + // Optional mapping for token URIs + mapping(uint256 => string) private _tokenURIs; + + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + _requireMinted(tokenId); + + string memory _tokenURI = _tokenURIs[tokenId]; + string memory base = _baseURI(); + + // If there is no base URI, return the token URI. + if (bytes(base).length == 0) { + return _tokenURI; + } + // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). + if (bytes(_tokenURI).length > 0) { + return string(abi.encodePacked(base, _tokenURI)); + } + + return super.tokenURI(tokenId); + } + + /** + * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { + require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); + _tokenURIs[tokenId] = _tokenURI; + } + + /** + * @dev See {ERC721-_burn}. This override additionally checks to see if a + * token-specific URI was set for the token, and if so, it deletes the token URI from + * the storage mapping. + */ + function _burn(uint256 tokenId) internal virtual override { + super._burn(tokenId); + + if (bytes(_tokenURIs[tokenId]).length != 0) { + delete _tokenURIs[tokenId]; + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol new file mode 100644 index 0000000..8e6500e --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Votes.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../../../governance/utils/Votes.sol"; + +/** + * @dev Extension of ERC721 to support voting and delegation as implemented by {Votes}, where each individual NFT counts + * as 1 vote unit. + * + * Tokens do not count as votes until they are delegated, because votes must be tracked which incurs an additional cost + * on every transfer. Token holders can either delegate to a trusted representative who will decide how to make use of + * the votes in governance decisions, or they can delegate to themselves to be their own representative. + * + * _Available since v4.5._ + */ +abstract contract ERC721Votes is ERC721, Votes { + /** + * @dev See {ERC721-_afterTokenTransfer}. Adjusts votes when tokens are transferred. + * + * Emits a {IVotes-DelegateVotesChanged} event. + */ + function _afterTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override { + _transferVotingUnits(from, to, batchSize); + super._afterTokenTransfer(from, to, firstTokenId, batchSize); + } + + /** + * @dev Returns the balance of `account`. + */ + function _getVotingUnits(address account) internal view virtual override returns (uint256) { + return balanceOf(account); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol new file mode 100644 index 0000000..dfea427 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol new file mode 100644 index 0000000..dca77ba --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) + +pragma solidity ^0.8.0; + +import "../IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/draft-ERC721Votes.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/draft-ERC721Votes.sol new file mode 100644 index 0000000..c6aa7c5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/extensions/draft-ERC721Votes.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/draft-ERC721Votes.sol) + +pragma solidity ^0.8.0; + +// ERC721Votes was marked as draft due to the EIP-712 dependency. +// EIP-712 is Final as of 2022-08-11. This file is deprecated. + +import "./ERC721Votes.sol"; diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol new file mode 100644 index 0000000..478e580 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol) + +pragma solidity ^0.8.0; + +import "../ERC721.sol"; +import "../extensions/ERC721Enumerable.sol"; +import "../extensions/ERC721Burnable.sol"; +import "../extensions/ERC721Pausable.sol"; +import "../../../access/AccessControlEnumerable.sol"; +import "../../../utils/Context.sol"; +import "../../../utils/Counters.sol"; + +/** + * @dev {ERC721} token, including: + * + * - ability for holders to burn (destroy) their tokens + * - a minter role that allows for token minting (creation) + * - a pauser role that allows to stop all token transfers + * - token ID and URI autogeneration + * + * This contract uses {AccessControl} to lock permissioned functions using the + * different roles - head to its documentation for details. + * + * The account that deploys the contract will be granted the minter and pauser + * roles, as well as the default admin role, which will let it grant both minter + * and pauser roles to other accounts. + * + * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ + */ +contract ERC721PresetMinterPauserAutoId is + Context, + AccessControlEnumerable, + ERC721Enumerable, + ERC721Burnable, + ERC721Pausable +{ + using Counters for Counters.Counter; + + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + Counters.Counter private _tokenIdTracker; + + string private _baseTokenURI; + + /** + * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the + * account that deploys the contract. + * + * Token URIs will be autogenerated based on `baseURI` and their token IDs. + * See {ERC721-tokenURI}. + */ + constructor(string memory name, string memory symbol, string memory baseTokenURI) ERC721(name, symbol) { + _baseTokenURI = baseTokenURI; + + _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); + + _setupRole(MINTER_ROLE, _msgSender()); + _setupRole(PAUSER_ROLE, _msgSender()); + } + + function _baseURI() internal view virtual override returns (string memory) { + return _baseTokenURI; + } + + /** + * @dev Creates a new token for `to`. Its token ID will be automatically + * assigned (and available on the emitted {IERC721-Transfer} event), and the token + * URI autogenerated based on the base URI passed at construction. + * + * See {ERC721-_mint}. + * + * Requirements: + * + * - the caller must have the `MINTER_ROLE`. + */ + function mint(address to) public virtual { + require(hasRole(MINTER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have minter role to mint"); + + // We cannot just use balanceOf to create the new tokenId because tokens + // can be burned (destroyed), so we need a separate counter. + _mint(to, _tokenIdTracker.current()); + _tokenIdTracker.increment(); + } + + /** + * @dev Pauses all token transfers. + * + * See {ERC721Pausable} and {Pausable-_pause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function pause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause"); + _pause(); + } + + /** + * @dev Unpauses all token transfers. + * + * See {ERC721Pausable} and {Pausable-_unpause}. + * + * Requirements: + * + * - the caller must have the `PAUSER_ROLE`. + */ + function unpause() public virtual { + require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause"); + _unpause(); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 firstTokenId, + uint256 batchSize + ) internal virtual override(ERC721, ERC721Enumerable, ERC721Pausable) { + super._beforeTokenTransfer(from, to, firstTokenId, batchSize); + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/presets/README.md b/lib/openzeppelin-contracts/contracts/token/ERC721/presets/README.md new file mode 100644 index 0000000..468200b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/presets/README.md @@ -0,0 +1 @@ +Contract presets are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com/) as a more powerful alternative. diff --git a/lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol b/lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol new file mode 100644 index 0000000..cfa533a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC721/utils/ERC721Holder.sol) + +pragma solidity ^0.8.0; + +import "../IERC721Receiver.sol"; + +/** + * @dev Implementation of the {IERC721Receiver} interface. + * + * Accepts all token transfers. + * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. + */ +contract ERC721Holder is IERC721Receiver { + /** + * @dev See {IERC721Receiver-onERC721Received}. + * + * Always returns `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { + return this.onERC721Received.selector; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC777/ERC777.sol b/lib/openzeppelin-contracts/contracts/token/ERC777/ERC777.sol new file mode 100644 index 0000000..c1503c4 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC777/ERC777.sol @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC777/ERC777.sol) + +pragma solidity ^0.8.0; + +import "./IERC777.sol"; +import "./IERC777Recipient.sol"; +import "./IERC777Sender.sol"; +import "../ERC20/IERC20.sol"; +import "../../utils/Address.sol"; +import "../../utils/Context.sol"; +import "../../utils/introspection/IERC1820Registry.sol"; + +/** + * @dev Implementation of the {IERC777} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * Support for ERC20 is included in this contract, as specified by the EIP: both + * the ERC777 and ERC20 interfaces can be safely used when interacting with it. + * Both {IERC777-Sent} and {IERC20-Transfer} events are emitted on token + * movements. + * + * Additionally, the {IERC777-granularity} value is hard-coded to `1`, meaning that there + * are no special restrictions in the amount of tokens that created, moved, or + * destroyed. This makes integration with ERC20 applications seamless. + */ +contract ERC777 is Context, IERC777, IERC20 { + using Address for address; + + IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + mapping(address => uint256) private _balances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + + bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender"); + bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); + + // This isn't ever read from - it's only used to respond to the defaultOperators query. + address[] private _defaultOperatorsArray; + + // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators). + mapping(address => bool) private _defaultOperators; + + // For each account, a mapping of its operators and revoked default operators. + mapping(address => mapping(address => bool)) private _operators; + mapping(address => mapping(address => bool)) private _revokedDefaultOperators; + + // ERC20-allowances + mapping(address => mapping(address => uint256)) private _allowances; + + /** + * @dev `defaultOperators` may be an empty array. + */ + constructor(string memory name_, string memory symbol_, address[] memory defaultOperators_) { + _name = name_; + _symbol = symbol_; + + _defaultOperatorsArray = defaultOperators_; + for (uint256 i = 0; i < defaultOperators_.length; i++) { + _defaultOperators[defaultOperators_[i]] = true; + } + + // register interfaces + _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this)); + _ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this)); + } + + /** + * @dev See {IERC777-name}. + */ + function name() public view virtual override returns (string memory) { + return _name; + } + + /** + * @dev See {IERC777-symbol}. + */ + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + /** + * @dev See {ERC20-decimals}. + * + * Always returns 18, as per the + * [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). + */ + function decimals() public pure virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC777-granularity}. + * + * This implementation always returns `1`. + */ + function granularity() public view virtual override returns (uint256) { + return 1; + } + + /** + * @dev See {IERC777-totalSupply}. + */ + function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) { + return _totalSupply; + } + + /** + * @dev Returns the amount of tokens owned by an account (`tokenHolder`). + */ + function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) { + return _balances[tokenHolder]; + } + + /** + * @dev See {IERC777-send}. + * + * Also emits a {IERC20-Transfer} event for ERC20 compatibility. + */ + function send(address recipient, uint256 amount, bytes memory data) public virtual override { + _send(_msgSender(), recipient, amount, data, "", true); + } + + /** + * @dev See {IERC20-transfer}. + * + * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} + * interface if it is a contract. + * + * Also emits a {Sent} event. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _send(_msgSender(), recipient, amount, "", "", false); + return true; + } + + /** + * @dev See {IERC777-burn}. + * + * Also emits a {IERC20-Transfer} event for ERC20 compatibility. + */ + function burn(uint256 amount, bytes memory data) public virtual override { + _burn(_msgSender(), amount, data, ""); + } + + /** + * @dev See {IERC777-isOperatorFor}. + */ + function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) { + return + operator == tokenHolder || + (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) || + _operators[tokenHolder][operator]; + } + + /** + * @dev See {IERC777-authorizeOperator}. + */ + function authorizeOperator(address operator) public virtual override { + require(_msgSender() != operator, "ERC777: authorizing self as operator"); + + if (_defaultOperators[operator]) { + delete _revokedDefaultOperators[_msgSender()][operator]; + } else { + _operators[_msgSender()][operator] = true; + } + + emit AuthorizedOperator(operator, _msgSender()); + } + + /** + * @dev See {IERC777-revokeOperator}. + */ + function revokeOperator(address operator) public virtual override { + require(operator != _msgSender(), "ERC777: revoking self as operator"); + + if (_defaultOperators[operator]) { + _revokedDefaultOperators[_msgSender()][operator] = true; + } else { + delete _operators[_msgSender()][operator]; + } + + emit RevokedOperator(operator, _msgSender()); + } + + /** + * @dev See {IERC777-defaultOperators}. + */ + function defaultOperators() public view virtual override returns (address[] memory) { + return _defaultOperatorsArray; + } + + /** + * @dev See {IERC777-operatorSend}. + * + * Emits {Sent} and {IERC20-Transfer} events. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) public virtual override { + require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder"); + _send(sender, recipient, amount, data, operatorData, true); + } + + /** + * @dev See {IERC777-operatorBurn}. + * + * Emits {Burned} and {IERC20-Transfer} events. + */ + function operatorBurn( + address account, + uint256 amount, + bytes memory data, + bytes memory operatorData + ) public virtual override { + require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder"); + _burn(account, amount, data, operatorData); + } + + /** + * @dev See {IERC20-allowance}. + * + * Note that operator and allowance concepts are orthogonal: operators may + * not have allowance, and accounts with allowance may not be operators + * themselves. + */ + function allowance(address holder, address spender) public view virtual override returns (uint256) { + return _allowances[holder][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Note that accounts cannot have allowance issued by their operators. + */ + function approve(address spender, uint256 value) public virtual override returns (bool) { + address holder = _msgSender(); + _approve(holder, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Note that operator and allowance concepts are orthogonal: operators cannot + * call `transferFrom` (unless they have allowance), and accounts with + * allowance cannot call `operatorSend` (unless they are operators). + * + * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. + */ + function transferFrom(address holder, address recipient, uint256 amount) public virtual override returns (bool) { + address spender = _msgSender(); + _spendAllowance(holder, spender, amount); + _send(holder, recipient, amount, "", "", false); + return true; + } + + /** + * @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with the caller address as the `operator` and with + * `userData` and `operatorData`. + * + * See {IERC777Sender} and {IERC777Recipient}. + * + * Emits {Minted} and {IERC20-Transfer} events. + * + * Requirements + * + * - `account` cannot be the zero address. + * - if `account` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function _mint(address account, uint256 amount, bytes memory userData, bytes memory operatorData) internal virtual { + _mint(account, amount, userData, operatorData, true); + } + + /** + * @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * If `requireReceptionAck` is set to true, and if a send hook is + * registered for `account`, the corresponding function will be called with + * `operator`, `data` and `operatorData`. + * + * See {IERC777Sender} and {IERC777Recipient}. + * + * Emits {Minted} and {IERC20-Transfer} events. + * + * Requirements + * + * - `account` cannot be the zero address. + * - if `account` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function _mint( + address account, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) internal virtual { + require(account != address(0), "ERC777: mint to the zero address"); + + address operator = _msgSender(); + + _beforeTokenTransfer(operator, address(0), account, amount); + + // Update state variables + _totalSupply += amount; + _balances[account] += amount; + + _callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck); + + emit Minted(operator, account, amount, userData, operatorData); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Send tokens + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient + */ + function _send( + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) internal virtual { + require(from != address(0), "ERC777: transfer from the zero address"); + require(to != address(0), "ERC777: transfer to the zero address"); + + address operator = _msgSender(); + + _callTokensToSend(operator, from, to, amount, userData, operatorData); + + _move(operator, from, to, amount, userData, operatorData); + + _callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck); + } + + /** + * @dev Burn tokens + * @param from address token holder address + * @param amount uint256 amount of tokens to burn + * @param data bytes extra information provided by the token holder + * @param operatorData bytes extra information provided by the operator (if any) + */ + function _burn(address from, uint256 amount, bytes memory data, bytes memory operatorData) internal virtual { + require(from != address(0), "ERC777: burn from the zero address"); + + address operator = _msgSender(); + + _callTokensToSend(operator, from, address(0), amount, data, operatorData); + + _beforeTokenTransfer(operator, from, address(0), amount); + + // Update state variables + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC777: burn amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _totalSupply -= amount; + + emit Burned(operator, from, amount, data, operatorData); + emit Transfer(from, address(0), amount); + } + + function _move( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) private { + _beforeTokenTransfer(operator, from, to, amount); + + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC777: transfer amount exceeds balance"); + unchecked { + _balances[from] = fromBalance - amount; + } + _balances[to] += amount; + + emit Sent(operator, from, to, amount, userData, operatorData); + emit Transfer(from, to, amount); + } + + /** + * @dev See {ERC20-_approve}. + * + * Note that accounts cannot have allowance issued by their operators. + */ + function _approve(address holder, address spender, uint256 value) internal virtual { + require(holder != address(0), "ERC777: approve from the zero address"); + require(spender != address(0), "ERC777: approve to the zero address"); + + _allowances[holder][spender] = value; + emit Approval(holder, spender, value); + } + + /** + * @dev Call from.tokensToSend() if the interface is registered + * @param operator address operator requesting the transfer + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + */ + function _callTokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData + ) private { + address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH); + if (implementer != address(0)) { + IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData); + } + } + + /** + * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but + * tokensReceived() was not registered for the recipient + * @param operator address operator requesting the transfer + * @param from address token holder address + * @param to address recipient address + * @param amount uint256 amount of tokens to transfer + * @param userData bytes extra information provided by the token holder (if any) + * @param operatorData bytes extra information provided by the operator (if any) + * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient + */ + function _callTokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes memory userData, + bytes memory operatorData, + bool requireReceptionAck + ) private { + address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH); + if (implementer != address(0)) { + IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData); + } else if (requireReceptionAck) { + require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient"); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `amount`. + * + * Does not update the allowance amount in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Might emit an {IERC20-Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC777: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + /** + * @dev Hook that is called before any token transfer. This includes + * calls to {send}, {transfer}, {operatorSend}, minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address operator, address from, address to, uint256 amount) internal virtual {} +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777.sol b/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777.sol new file mode 100644 index 0000000..d3bede6 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC777/IERC777.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777Token standard as defined in the EIP. + * + * This contract uses the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let + * token holders and recipients react to token movements by using setting implementers + * for the associated interfaces in said registry. See {IERC1820Registry} and + * {ERC1820Implementer}. + */ +interface IERC777 { + /** + * @dev Emitted when `amount` tokens are created by `operator` and assigned to `to`. + * + * Note that some additional user `data` and `operatorData` can be logged in the event. + */ + event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); + + /** + * @dev Emitted when `operator` destroys `amount` tokens from `account`. + * + * Note that some additional user `data` and `operatorData` can be logged in the event. + */ + event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); + + /** + * @dev Emitted when `operator` is made operator for `tokenHolder`. + */ + event AuthorizedOperator(address indexed operator, address indexed tokenHolder); + + /** + * @dev Emitted when `operator` is revoked its operator status for `tokenHolder`. + */ + event RevokedOperator(address indexed operator, address indexed tokenHolder); + + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the smallest part of the token that is not divisible. This + * means all token operations (creation, movement and destruction) must have + * amounts that are a multiple of this number. + * + * For most token contracts, this value will equal 1. + */ + function granularity() external view returns (uint256); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by an account (`owner`). + */ + function balanceOf(address owner) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * If send or receive hooks are registered for the caller and `recipient`, + * the corresponding functions will be called with `data` and empty + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function send(address recipient, uint256 amount, bytes calldata data) external; + + /** + * @dev Destroys `amount` tokens from the caller's account, reducing the + * total supply. + * + * If a send hook is registered for the caller, the corresponding function + * will be called with `data` and empty `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + */ + function burn(uint256 amount, bytes calldata data) external; + + /** + * @dev Returns true if an account is an operator of `tokenHolder`. + * Operators can send and burn tokens on behalf of their owners. All + * accounts are their own operator. + * + * See {operatorSend} and {operatorBurn}. + */ + function isOperatorFor(address operator, address tokenHolder) external view returns (bool); + + /** + * @dev Make an account an operator of the caller. + * + * See {isOperatorFor}. + * + * Emits an {AuthorizedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function authorizeOperator(address operator) external; + + /** + * @dev Revoke an account's operator status for the caller. + * + * See {isOperatorFor} and {defaultOperators}. + * + * Emits a {RevokedOperator} event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function revokeOperator(address operator) external; + + /** + * @dev Returns the list of default operators. These accounts are operators + * for all token holders, even if {authorizeOperator} was never called on + * them. + * + * This list is immutable, but individual holders may revoke these via + * {revokeOperator}, in which case {isOperatorFor} will return false. + */ + function defaultOperators() external view returns (address[] memory); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must + * be an operator of `sender`. + * + * If send or receive hooks are registered for `sender` and `recipient`, + * the corresponding functions will be called with `data` and + * `operatorData`. See {IERC777Sender} and {IERC777Recipient}. + * + * Emits a {Sent} event. + * + * Requirements + * + * - `sender` cannot be the zero address. + * - `sender` must have at least `amount` tokens. + * - the caller must be an operator for `sender`. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the {IERC777Recipient} + * interface. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + /** + * @dev Destroys `amount` tokens from `account`, reducing the total supply. + * The caller must be an operator of `account`. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `data` and `operatorData`. See {IERC777Sender}. + * + * Emits a {Burned} event. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + * - the caller must be an operator for `account`. + */ + function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external; + + event Sent( + address indexed operator, + address indexed from, + address indexed to, + uint256 amount, + bytes data, + bytes operatorData + ); +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777Recipient.sol b/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777Recipient.sol new file mode 100644 index 0000000..717dd8f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777Recipient.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Recipient.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP. + * + * Accounts can be notified of {IERC777} tokens being sent to them by having a + * contract implement this interface (contract holders can be their own + * implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Recipient { + /** + * @dev Called by an {IERC777} token contract whenever tokens are being + * moved or created into a registered account (`to`). The type of operation + * is conveyed by `from` being the zero address or not. + * + * This call occurs _after_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the post-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777Sender.sol b/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777Sender.sol new file mode 100644 index 0000000..969e3e3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC777/IERC777Sender.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/IERC777Sender.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC777TokensSender standard as defined in the EIP. + * + * {IERC777} Token holders can be notified of operations performed on their + * tokens by having a contract implement this interface (contract holders can be + * their own implementer) and registering it on the + * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry]. + * + * See {IERC1820Registry} and {ERC1820Implementer}. + */ +interface IERC777Sender { + /** + * @dev Called by an {IERC777} token contract whenever a registered holder's + * (`from`) tokens are about to be moved or destroyed. The type of operation + * is conveyed by `to` being the zero address or not. + * + * This call occurs _before_ the token contract's state is updated, so + * {IERC777-balanceOf}, etc., can be used to query the pre-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC777/README.adoc b/lib/openzeppelin-contracts/contracts/token/ERC777/README.adoc new file mode 100644 index 0000000..5012a31 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC777/README.adoc @@ -0,0 +1,30 @@ += ERC 777 + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/token/erc777 + +This set of interfaces and contracts are all related to the https://eips.ethereum.org/EIPS/eip-777[ERC777 token standard]. + +TIP: For an overview of ERC777 tokens and a walk through on how to create a token contract read our xref:ROOT:erc777.adoc[ERC777 guide]. + +The token behavior itself is implemented in the core contracts: {IERC777}, {ERC777}. + +Additionally there are interfaces used to develop contracts that react to token movements: {IERC777Sender}, {IERC777Recipient}. + +== Core + +{{IERC777}} + +{{ERC777}} + +== Hooks + +{{IERC777Sender}} + +{{IERC777Recipient}} + +== Presets + +These contracts are preconfigured combinations of features. They can be used through inheritance or as models to copy and paste their source code. + +{{ERC777PresetFixedSupply}} diff --git a/lib/openzeppelin-contracts/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol b/lib/openzeppelin-contracts/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol new file mode 100644 index 0000000..8bd4b79 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/ERC777/presets/ERC777PresetFixedSupply.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC777/presets/ERC777PresetFixedSupply.sol) +pragma solidity ^0.8.0; + +import "../ERC777.sol"; + +/** + * @dev {ERC777} token, including: + * + * - Preminted initial supply + * - No access control mechanism (for minting/pausing) and hence no governance + * + * _Available since v3.4._ + */ +contract ERC777PresetFixedSupply is ERC777 { + /** + * @dev Mints `initialSupply` amount of token and transfers them to `owner`. + * + * See {ERC777-constructor}. + */ + constructor( + string memory name, + string memory symbol, + address[] memory defaultOperators, + uint256 initialSupply, + address owner + ) ERC777(name, symbol, defaultOperators) { + _mint(owner, initialSupply, "", ""); + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/common/ERC2981.sol b/lib/openzeppelin-contracts/contracts/token/common/ERC2981.sol new file mode 100644 index 0000000..84cb6b8 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/common/ERC2981.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (token/common/ERC2981.sol) + +pragma solidity ^0.8.0; + +import "../../interfaces/IERC2981.sol"; +import "../../utils/introspection/ERC165.sol"; + +/** + * @dev Implementation of the NFT Royalty Standard, a standardized way to retrieve royalty payment information. + * + * Royalty information can be specified globally for all token ids via {_setDefaultRoyalty}, and/or individually for + * specific token ids via {_setTokenRoyalty}. The latter takes precedence over the first. + * + * Royalty is specified as a fraction of sale price. {_feeDenominator} is overridable but defaults to 10000, meaning the + * fee is specified in basis points by default. + * + * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See + * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to + * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported. + * + * _Available since v4.5._ + */ +abstract contract ERC2981 is IERC2981, ERC165 { + struct RoyaltyInfo { + address receiver; + uint96 royaltyFraction; + } + + RoyaltyInfo private _defaultRoyaltyInfo; + mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { + return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId); + } + + /** + * @inheritdoc IERC2981 + */ + function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) { + RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId]; + + if (royalty.receiver == address(0)) { + royalty = _defaultRoyaltyInfo; + } + + uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator(); + + return (royalty.receiver, royaltyAmount); + } + + /** + * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a + * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an + * override. + */ + function _feeDenominator() internal pure virtual returns (uint96) { + return 10000; + } + + /** + * @dev Sets the royalty information that all ids in this contract will default to. + * + * Requirements: + * + * - `receiver` cannot be the zero address. + * - `feeNumerator` cannot be greater than the fee denominator. + */ + function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual { + require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice"); + require(receiver != address(0), "ERC2981: invalid receiver"); + + _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator); + } + + /** + * @dev Removes default royalty information. + */ + function _deleteDefaultRoyalty() internal virtual { + delete _defaultRoyaltyInfo; + } + + /** + * @dev Sets the royalty information for a specific token id, overriding the global default. + * + * Requirements: + * + * - `receiver` cannot be the zero address. + * - `feeNumerator` cannot be greater than the fee denominator. + */ + function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) internal virtual { + require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice"); + require(receiver != address(0), "ERC2981: Invalid parameters"); + + _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator); + } + + /** + * @dev Resets royalty information for the token id back to the global default. + */ + function _resetTokenRoyalty(uint256 tokenId) internal virtual { + delete _tokenRoyaltyInfo[tokenId]; + } +} diff --git a/lib/openzeppelin-contracts/contracts/token/common/README.adoc b/lib/openzeppelin-contracts/contracts/token/common/README.adoc new file mode 100644 index 0000000..af61674 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/token/common/README.adoc @@ -0,0 +1,10 @@ += Common (Tokens) + +Functionality that is common to multiple token standards. + +* {ERC2981}: NFT Royalties compatible with both ERC721 and ERC1155. +** For ERC721 consider {ERC721Royalty} which clears the royalty information from storage on burn. + +== Contracts + +{{ERC2981}} diff --git a/lib/openzeppelin-contracts/contracts/utils/Address.sol b/lib/openzeppelin-contracts/contracts/utils/Address.sol new file mode 100644 index 0000000..70d03e3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Address.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) + +pragma solidity ^0.8.1; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling + * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. + * + * _Available since v4.8._ + */ + function verifyCallResultFromTarget( + address target, + bool success, + bytes memory returndata, + string memory errorMessage + ) internal view returns (bytes memory) { + if (success) { + if (returndata.length == 0) { + // only check isContract if the call was successful and the return data is empty + // otherwise we already know that it was a contract + require(isContract(target), "Address: call to non-contract"); + } + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + /** + * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason or using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function _revert(bytes memory returndata, string memory errorMessage) private pure { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Arrays.sol b/lib/openzeppelin-contracts/contracts/utils/Arrays.sol new file mode 100644 index 0000000..2496475 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Arrays.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Arrays.sol) + +pragma solidity ^0.8.0; + +import "./StorageSlot.sol"; +import "./math/Math.sol"; + +/** + * @dev Collection of functions related to array types. + */ +library Arrays { + using StorageSlot for bytes32; + + /** + * @dev Searches a sorted `array` and returns the first index that contains + * a value greater or equal to `element`. If no such index exists (i.e. all + * values in the array are strictly less than `element`), the array length is + * returned. Time complexity O(log n). + * + * `array` is expected to be sorted in ascending order, and to contain no + * repeated elements. + */ + function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { + if (array.length == 0) { + return 0; + } + + uint256 low = 0; + uint256 high = array.length; + + while (low < high) { + uint256 mid = Math.average(low, high); + + // Note that mid will always be strictly less than high (i.e. it will be a valid array index) + // because Math.average rounds down (it does integer division with truncation). + if (unsafeAccess(array, mid).value > element) { + high = mid; + } else { + low = mid + 1; + } + } + + // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. + if (low > 0 && unsafeAccess(array, low - 1).value == element) { + return low - 1; + } else { + return low; + } + } + + /** + * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. + * + * WARNING: Only use if you are certain `pos` is lower than the array length. + */ + function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) { + bytes32 slot; + // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` + // following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + + /// @solidity memory-safe-assembly + assembly { + mstore(0, arr.slot) + slot := add(keccak256(0, 0x20), pos) + } + return slot.getAddressSlot(); + } + + /** + * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. + * + * WARNING: Only use if you are certain `pos` is lower than the array length. + */ + function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) { + bytes32 slot; + // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` + // following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + + /// @solidity memory-safe-assembly + assembly { + mstore(0, arr.slot) + slot := add(keccak256(0, 0x20), pos) + } + return slot.getBytes32Slot(); + } + + /** + * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check. + * + * WARNING: Only use if you are certain `pos` is lower than the array length. + */ + function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) { + bytes32 slot; + // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr` + // following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays. + + /// @solidity memory-safe-assembly + assembly { + mstore(0, arr.slot) + slot := add(keccak256(0, 0x20), pos) + } + return slot.getUint256Slot(); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Base64.sol b/lib/openzeppelin-contracts/contracts/utils/Base64.sol new file mode 100644 index 0000000..4e08cd5 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Base64.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides a set of functions to operate with Base64 strings. + * + * _Available since v4.5._ + */ +library Base64 { + /** + * @dev Base64 Encoding/Decoding Table + */ + string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /** + * @dev Converts a `bytes` to its Bytes64 `string` representation. + */ + function encode(bytes memory data) internal pure returns (string memory) { + /** + * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence + * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol + */ + if (data.length == 0) return ""; + + // Loads the table into memory + string memory table = _TABLE; + + // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter + // and split into 4 numbers of 6 bits. + // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up + // - `data.length + 2` -> Round up + // - `/ 3` -> Number of 3-bytes chunks + // - `4 *` -> 4 characters for each chunk + string memory result = new string(4 * ((data.length + 2) / 3)); + + /// @solidity memory-safe-assembly + assembly { + // Prepare the lookup table (skip the first "length" byte) + let tablePtr := add(table, 1) + + // Prepare result pointer, jump over length + let resultPtr := add(result, 32) + + // Run over the input, 3 bytes at a time + for { + let dataPtr := data + let endPtr := add(data, mload(data)) + } lt(dataPtr, endPtr) { + + } { + // Advance 3 bytes + dataPtr := add(dataPtr, 3) + let input := mload(dataPtr) + + // To write each character, shift the 3 bytes (18 bits) chunk + // 4 times in blocks of 6 bits for each character (18, 12, 6, 0) + // and apply logical AND with 0x3F which is the number of + // the previous character in the ASCII table prior to the Base64 Table + // The result is then added to the table to get the character to write, + // and finally write it in the result pointer but with a left shift + // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits + + mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + + mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + + mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + + mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F)))) + resultPtr := add(resultPtr, 1) // Advance + } + + // When data `bytes` is not exactly 3 bytes long + // it is padded with `=` characters at the end + switch mod(mload(data), 3) + case 1 { + mstore8(sub(resultPtr, 1), 0x3d) + mstore8(sub(resultPtr, 2), 0x3d) + } + case 2 { + mstore8(sub(resultPtr, 1), 0x3d) + } + } + + return result; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Checkpoints.sol b/lib/openzeppelin-contracts/contracts/utils/Checkpoints.sol new file mode 100644 index 0000000..0e0a136 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Checkpoints.sol @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Checkpoints.sol) +// This file was procedurally generated from scripts/generate/templates/Checkpoints.js. + +pragma solidity ^0.8.0; + +import "./math/Math.sol"; +import "./math/SafeCast.sol"; + +/** + * @dev This library defines the `History` struct, for checkpointing values as they change at different points in + * time, and later looking up past values by block number. See {Votes} as an example. + * + * To create a history of checkpoints define a variable type `Checkpoints.History` in your contract, and store a new + * checkpoint for the current transaction block using the {push} function. + * + * _Available since v4.5._ + */ +library Checkpoints { + struct History { + Checkpoint[] _checkpoints; + } + + struct Checkpoint { + uint32 _blockNumber; + uint224 _value; + } + + /** + * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one + * before it is returned, or zero otherwise. Because the number returned corresponds to that at the end of the + * block, the requested block number must be in the past, excluding the current block. + */ + function getAtBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { + require(blockNumber < block.number, "Checkpoints: block not yet mined"); + uint32 key = SafeCast.toUint32(blockNumber); + + uint256 len = self._checkpoints.length; + uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one + * before it is returned, or zero otherwise. Similar to {upperLookup} but optimized for the case when the searched + * checkpoint is probably "recent", defined as being among the last sqrt(N) checkpoints where N is the number of + * checkpoints. + */ + function getAtProbablyRecentBlock(History storage self, uint256 blockNumber) internal view returns (uint256) { + require(blockNumber < block.number, "Checkpoints: block not yet mined"); + uint32 key = SafeCast.toUint32(blockNumber); + + uint256 len = self._checkpoints.length; + + uint256 low = 0; + uint256 high = len; + + if (len > 5) { + uint256 mid = len - Math.sqrt(len); + if (key < _unsafeAccess(self._checkpoints, mid)._blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); + + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block. + * + * Returns previous value and new value. + */ + function push(History storage self, uint256 value) internal returns (uint256, uint256) { + return _insert(self._checkpoints, SafeCast.toUint32(block.number), SafeCast.toUint224(value)); + } + + /** + * @dev Pushes a value onto a History, by updating the latest value using binary operation `op`. The new value will + * be set to `op(latest, delta)`. + * + * Returns previous value and new value. + */ + function push( + History storage self, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) internal returns (uint256, uint256) { + return push(self, op(latest(self), delta)); + } + + /** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ + function latest(History storage self) internal view returns (uint224) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ + function latestCheckpoint( + History storage self + ) internal view returns (bool exists, uint32 _blockNumber, uint224 _value) { + uint256 pos = self._checkpoints.length; + if (pos == 0) { + return (false, 0, 0); + } else { + Checkpoint memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); + return (true, ckpt._blockNumber, ckpt._value); + } + } + + /** + * @dev Returns the number of checkpoint. + */ + function length(History storage self) internal view returns (uint256) { + return self._checkpoints.length; + } + + /** + * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ + function _insert(Checkpoint[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + Checkpoint memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + require(last._blockNumber <= key, "Checkpoint: decreasing keys"); + + // Update or push new checkpoint + if (last._blockNumber == key) { + _unsafeAccess(self, pos - 1)._value = value; + } else { + self.push(Checkpoint({_blockNumber: key, _value: value})); + } + return (last._value, value); + } else { + self.push(Checkpoint({_blockNumber: key, _value: value})); + return (0, value); + } + } + + /** + * @dev Return the index of the oldest checkpoint whose key is greater than the search key, or `high` if there is none. + * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _upperBinaryLookup( + Checkpoint[] storage self, + uint32 key, + uint256 low, + uint256 high + ) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._blockNumber > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; + } + + /** + * @dev Return the index of the oldest checkpoint whose key is greater or equal than the search key, or `high` if there is none. + * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _lowerBinaryLookup( + Checkpoint[] storage self, + uint32 key, + uint256 low, + uint256 high + ) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._blockNumber < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint[] storage self, uint256 pos) private pure returns (Checkpoint storage result) { + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } + + struct Trace224 { + Checkpoint224[] _checkpoints; + } + + struct Checkpoint224 { + uint32 _key; + uint224 _value; + } + + /** + * @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint. + * + * Returns previous value and new value. + */ + function push(Trace224 storage self, uint32 key, uint224 value) internal returns (uint224, uint224) { + return _insert(self._checkpoints, key, value); + } + + /** + * @dev Returns the value in the oldest checkpoint with key greater or equal than the search key, or zero if there is none. + */ + function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) { + uint256 len = self._checkpoints.length; + uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); + return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint with key lower or equal than the search key. + */ + function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) { + uint256 len = self._checkpoints.length; + uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ + function latest(Trace224 storage self) internal view returns (uint224) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ + function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) { + uint256 pos = self._checkpoints.length; + if (pos == 0) { + return (false, 0, 0); + } else { + Checkpoint224 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); + return (true, ckpt._key, ckpt._value); + } + } + + /** + * @dev Returns the number of checkpoint. + */ + function length(Trace224 storage self) internal view returns (uint256) { + return self._checkpoints.length; + } + + /** + * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ + function _insert(Checkpoint224[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + Checkpoint224 memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + require(last._key <= key, "Checkpoint: decreasing keys"); + + // Update or push new checkpoint + if (last._key == key) { + _unsafeAccess(self, pos - 1)._value = value; + } else { + self.push(Checkpoint224({_key: key, _value: value})); + } + return (last._value, value); + } else { + self.push(Checkpoint224({_key: key, _value: value})); + return (0, value); + } + } + + /** + * @dev Return the index of the oldest checkpoint whose key is greater than the search key, or `high` if there is none. + * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _upperBinaryLookup( + Checkpoint224[] storage self, + uint32 key, + uint256 low, + uint256 high + ) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; + } + + /** + * @dev Return the index of the oldest checkpoint whose key is greater or equal than the search key, or `high` if there is none. + * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _lowerBinaryLookup( + Checkpoint224[] storage self, + uint32 key, + uint256 low, + uint256 high + ) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess( + Checkpoint224[] storage self, + uint256 pos + ) private pure returns (Checkpoint224 storage result) { + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } + + struct Trace160 { + Checkpoint160[] _checkpoints; + } + + struct Checkpoint160 { + uint96 _key; + uint160 _value; + } + + /** + * @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint. + * + * Returns previous value and new value. + */ + function push(Trace160 storage self, uint96 key, uint160 value) internal returns (uint160, uint160) { + return _insert(self._checkpoints, key, value); + } + + /** + * @dev Returns the value in the oldest checkpoint with key greater or equal than the search key, or zero if there is none. + */ + function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) { + uint256 len = self._checkpoints.length; + uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); + return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint with key lower or equal than the search key. + */ + function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) { + uint256 len = self._checkpoints.length; + uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ + function latest(Trace160 storage self) internal view returns (uint160) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ + function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) { + uint256 pos = self._checkpoints.length; + if (pos == 0) { + return (false, 0, 0); + } else { + Checkpoint160 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); + return (true, ckpt._key, ckpt._value); + } + } + + /** + * @dev Returns the number of checkpoint. + */ + function length(Trace160 storage self) internal view returns (uint256) { + return self._checkpoints.length; + } + + /** + * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ + function _insert(Checkpoint160[] storage self, uint96 key, uint160 value) private returns (uint160, uint160) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + Checkpoint160 memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + require(last._key <= key, "Checkpoint: decreasing keys"); + + // Update or push new checkpoint + if (last._key == key) { + _unsafeAccess(self, pos - 1)._value = value; + } else { + self.push(Checkpoint160({_key: key, _value: value})); + } + return (last._value, value); + } else { + self.push(Checkpoint160({_key: key, _value: value})); + return (0, value); + } + } + + /** + * @dev Return the index of the oldest checkpoint whose key is greater than the search key, or `high` if there is none. + * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _upperBinaryLookup( + Checkpoint160[] storage self, + uint96 key, + uint256 low, + uint256 high + ) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; + } + + /** + * @dev Return the index of the oldest checkpoint whose key is greater or equal than the search key, or `high` if there is none. + * `low` and `high` define a section where to do the search, with inclusive `low` and exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _lowerBinaryLookup( + Checkpoint160[] storage self, + uint96 key, + uint256 low, + uint256 high + ) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess( + Checkpoint160[] storage self, + uint256 pos + ) private pure returns (Checkpoint160 storage result) { + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Context.sol b/lib/openzeppelin-contracts/contracts/utils/Context.sol new file mode 100644 index 0000000..f304065 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Context.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Counters.sol b/lib/openzeppelin-contracts/contracts/utils/Counters.sol new file mode 100644 index 0000000..8a4f2a2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Counters.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) + +pragma solidity ^0.8.0; + +/** + * @title Counters + * @author Matt Condon (@shrugs) + * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number + * of elements in a mapping, issuing ERC721 ids, or counting request ids. + * + * Include with `using Counters for Counters.Counter;` + */ +library Counters { + struct Counter { + // This variable should never be directly accessed by users of the library: interactions must be restricted to + // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add + // this feature: see https://github.com/ethereum/solidity/issues/4637 + uint256 _value; // default: 0 + } + + function current(Counter storage counter) internal view returns (uint256) { + return counter._value; + } + + function increment(Counter storage counter) internal { + unchecked { + counter._value += 1; + } + } + + function decrement(Counter storage counter) internal { + uint256 value = counter._value; + require(value > 0, "Counter: decrement overflow"); + unchecked { + counter._value = value - 1; + } + } + + function reset(Counter storage counter) internal { + counter._value = 0; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Create2.sol b/lib/openzeppelin-contracts/contracts/utils/Create2.sol new file mode 100644 index 0000000..2255a4d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Create2.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Create2.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. + * `CREATE2` can be used to compute in advance the address where a smart + * contract will be deployed, which allows for interesting new mechanisms known + * as 'counterfactual interactions'. + * + * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more + * information. + */ +library Create2 { + /** + * @dev Deploys a contract using `CREATE2`. The address where the contract + * will be deployed can be known in advance via {computeAddress}. + * + * The bytecode for a contract can be obtained from Solidity with + * `type(contractName).creationCode`. + * + * Requirements: + * + * - `bytecode` must not be empty. + * - `salt` must have not been used for `bytecode` already. + * - the factory must have a balance of at least `amount`. + * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. + */ + function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) { + require(address(this).balance >= amount, "Create2: insufficient balance"); + require(bytecode.length != 0, "Create2: bytecode length is zero"); + /// @solidity memory-safe-assembly + assembly { + addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) + } + require(addr != address(0), "Create2: Failed on deploy"); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the + * `bytecodeHash` or `salt` will result in a new destination address. + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { + return computeAddress(salt, bytecodeHash, address(this)); + } + + /** + * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at + * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. + */ + function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) { + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) // Get free memory pointer + + // | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... | + // |-------------------|---------------------------------------------------------------------------| + // | bytecodeHash | CCCCCCCCCCCCC...CC | + // | salt | BBBBBBBBBBBBB...BB | + // | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA | + // | 0xFF | FF | + // |-------------------|---------------------------------------------------------------------------| + // | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC | + // | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ | + + mstore(add(ptr, 0x40), bytecodeHash) + mstore(add(ptr, 0x20), salt) + mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes + let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff + mstore8(start, 0xff) + addr := keccak256(start, 85) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Multicall.sol b/lib/openzeppelin-contracts/contracts/utils/Multicall.sol new file mode 100644 index 0000000..bdb8201 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Multicall.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol) + +pragma solidity ^0.8.0; + +import "./Address.sol"; + +/** + * @dev Provides a function to batch together multiple calls in a single external call. + * + * _Available since v4.1._ + */ +abstract contract Multicall { + /** + * @dev Receives and executes a batch of function calls on this contract. + */ + function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { + results = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + results[i] = Address.functionDelegateCall(address(this), data[i]); + } + return results; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/README.adoc b/lib/openzeppelin-contracts/contracts/utils/README.adoc new file mode 100644 index 0000000..7fef825 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/README.adoc @@ -0,0 +1,111 @@ += Utilities + +[.readme-notice] +NOTE: This document is better viewed at https://docs.openzeppelin.com/contracts/api/utils + +Miscellaneous contracts and libraries containing utility functions you can use to improve security, work with new data types, or safely use low-level primitives. + +The {Address}, {Arrays}, {Base64} and {Strings} libraries provide more operations related to these native data types, while {SafeCast} adds ways to safely convert between the different signed and unsigned numeric types. +{Multicall} provides a function to batch together multiple calls in a single external call. + +For new data types: + + * {Counters}: a simple way to get a counter that can only be incremented, decremented or reset. Very useful for ID generation, counting contract activity, among others. + * {EnumerableMap}: like Solidity's https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] type, but with key-value _enumeration_: this will let you know how many entries a mapping has, and iterate over them (which is not possible with `mapping`). + * {EnumerableSet}: like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc. + +[NOTE] +==== +Because Solidity does not support generic types, {EnumerableMap} and {EnumerableSet} are specialized to a limited number of key-value types. + +As of v3.0, {EnumerableMap} supports `uint256 -> address` (`UintToAddressMap`), and {EnumerableSet} supports `address` and `uint256` (`AddressSet` and `UintSet`). +==== + +Finally, {Create2} contains all necessary utilities to safely use the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode], without having to deal with low-level assembly. + +== Math + +{{Math}} + +{{SignedMath}} + +{{SafeCast}} + +{{SafeMath}} + +{{SignedSafeMath}} + +== Cryptography + +{{ECDSA}} + +{{SignatureChecker}} + +{{MerkleProof}} + +{{EIP712}} + +== Escrow + +{{ConditionalEscrow}} + +{{Escrow}} + +{{RefundEscrow}} + +== Introspection + +This set of interfaces and contracts deal with https://en.wikipedia.org/wiki/Type_introspection[type introspection] of contracts, that is, examining which functions can be called on them. This is usually referred to as a contract's _interface_. + +Ethereum contracts have no native concept of an interface, so applications must usually simply trust they are not making an incorrect call. For trusted setups this is a non-issue, but often unknown and untrusted third-party addresses need to be interacted with. There may even not be any direct calls to them! (e.g. `ERC20` tokens may be sent to a contract that lacks a way to transfer them out of it, locking them forever). In these cases, a contract _declaring_ its interface can be very helpful in preventing errors. + +There are two main ways to approach this. + +* Locally, where a contract implements `IERC165` and declares an interface, and a second one queries it directly via `ERC165Checker`. +* Globally, where a global and unique registry (`IERC1820Registry`) is used to register implementers of a certain interface (`IERC1820Implementer`). It is then the registry that is queried, which allows for more complex setups, like contracts implementing interfaces for externally-owned accounts. + +Note that, in all cases, accounts simply _declare_ their interfaces, but they are not required to actually implement them. This mechanism can therefore be used to both prevent errors and allow for complex interactions (see `ERC777`), but it must not be relied on for security. + +{{IERC165}} + +{{ERC165}} + +{{ERC165Storage}} + +{{ERC165Checker}} + +{{IERC1820Registry}} + +{{IERC1820Implementer}} + +{{ERC1820Implementer}} + +== Data Structures + +{{BitMaps}} + +{{EnumerableMap}} + +{{EnumerableSet}} + +{{DoubleEndedQueue}} + +{{Checkpoints}} + +== Libraries + +{{Create2}} + +{{Address}} + +{{Arrays}} + +{{Base64}} + +{{Counters}} + +{{Strings}} + +{{StorageSlot}} + +{{Multicall}} diff --git a/lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol b/lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol new file mode 100644 index 0000000..6ab8f5d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/StorageSlot.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Library for reading and writing primitive types to specific storage slots. + * + * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. + * This library helps with reading and writing to such slots without the need for inline assembly. + * + * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. + * + * Example usage to set ERC1967 implementation slot: + * ``` + * contract ERC1967 { + * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + * + * function _getImplementation() internal view returns (address) { + * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + * } + * + * function _setImplementation(address newImplementation) internal { + * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + * } + * } + * ``` + * + * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ + */ +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /** + * @dev Returns an `AddressSlot` with member `value` located at `slot`. + */ + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `BooleanSlot` with member `value` located at `slot`. + */ + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + */ + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Uint256Slot` with member `value` located at `slot`. + */ + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Strings.sol b/lib/openzeppelin-contracts/contracts/utils/Strings.sol new file mode 100644 index 0000000..76aa364 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Strings.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) + +pragma solidity ^0.8.0; + +import "./math/Math.sol"; + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _SYMBOLS = "0123456789abcdef"; + uint8 private constant _ADDRESS_LENGTH = 20; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + unchecked { + uint256 length = Math.log10(value) + 1; + string memory buffer = new string(length); + uint256 ptr; + /// @solidity memory-safe-assembly + assembly { + ptr := add(buffer, add(32, length)) + } + while (true) { + ptr--; + /// @solidity memory-safe-assembly + assembly { + mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) + } + value /= 10; + if (value == 0) break; + } + return buffer; + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + unchecked { + return toHexString(value, Math.log256(value) + 1); + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. + */ + function toHexString(address addr) internal pure returns (string memory) { + return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/Timers.sol b/lib/openzeppelin-contracts/contracts/utils/Timers.sol new file mode 100644 index 0000000..4bc86f2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/Timers.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/Timers.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Tooling for timepoints, timers and delays + */ +library Timers { + struct Timestamp { + uint64 _deadline; + } + + function getDeadline(Timestamp memory timer) internal pure returns (uint64) { + return timer._deadline; + } + + function setDeadline(Timestamp storage timer, uint64 timestamp) internal { + timer._deadline = timestamp; + } + + function reset(Timestamp storage timer) internal { + timer._deadline = 0; + } + + function isUnset(Timestamp memory timer) internal pure returns (bool) { + return timer._deadline == 0; + } + + function isStarted(Timestamp memory timer) internal pure returns (bool) { + return timer._deadline > 0; + } + + function isPending(Timestamp memory timer) internal view returns (bool) { + return timer._deadline > block.timestamp; + } + + function isExpired(Timestamp memory timer) internal view returns (bool) { + return isStarted(timer) && timer._deadline <= block.timestamp; + } + + struct BlockNumber { + uint64 _deadline; + } + + function getDeadline(BlockNumber memory timer) internal pure returns (uint64) { + return timer._deadline; + } + + function setDeadline(BlockNumber storage timer, uint64 timestamp) internal { + timer._deadline = timestamp; + } + + function reset(BlockNumber storage timer) internal { + timer._deadline = 0; + } + + function isUnset(BlockNumber memory timer) internal pure returns (bool) { + return timer._deadline == 0; + } + + function isStarted(BlockNumber memory timer) internal pure returns (bool) { + return timer._deadline > 0; + } + + function isPending(BlockNumber memory timer) internal view returns (bool) { + return timer._deadline > block.number; + } + + function isExpired(BlockNumber memory timer) internal view returns (bool) { + return isStarted(timer) && timer._deadline <= block.number; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol b/lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol new file mode 100644 index 0000000..3f99652 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol) + +pragma solidity ^0.8.0; + +import "../Strings.sol"; + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + enum RecoverError { + NoError, + InvalidSignature, + InvalidSignatureLength, + InvalidSignatureS, + InvalidSignatureV // Deprecated in v4.8 + } + + function _throwError(RecoverError error) private pure { + if (error == RecoverError.NoError) { + return; // no error: do nothing + } else if (error == RecoverError.InvalidSignature) { + revert("ECDSA: invalid signature"); + } else if (error == RecoverError.InvalidSignatureLength) { + revert("ECDSA: invalid signature length"); + } else if (error == RecoverError.InvalidSignatureS) { + revert("ECDSA: invalid signature 's' value"); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature` or error string. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + * + * Documentation for signature generation: + * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] + * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] + * + * _Available since v4.3._ + */ + function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { + if (signature.length == 65) { + bytes32 r; + bytes32 s; + uint8 v; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return tryRecover(hash, v, r, s); + } else { + return (address(0), RecoverError.InvalidSignatureLength); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, signature); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. + * + * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + * + * _Available since v4.3._ + */ + function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { + bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + uint8 v = uint8((uint256(vs) >> 255) + 27); + return tryRecover(hash, v, r, s); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. + * + * _Available since v4.2._ + */ + function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, r, vs); + _throwError(error); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `v`, + * `r` and `s` signature fields separately. + * + * _Available since v4.3._ + */ + function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + return (address(0), RecoverError.InvalidSignatureS); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + if (signer == address(0)) { + return (address(0), RecoverError.InvalidSignature); + } + + return (signer, RecoverError.NoError); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { + (address recovered, RecoverError error) = tryRecover(hash, v, r, s); + _throwError(error); + return recovered; + } + + /** + * @dev Returns an Ethereum Signed Message, created from a `hash`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { + // 32 is the length in bytes of hash, + // enforced by the type signature above + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + + /** + * @dev Returns an Ethereum Signed Message, created from `s`. This + * produces hash corresponding to the one signed with the + * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] + * JSON-RPC method as part of EIP-191. + * + * See {recover}. + */ + function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); + } + + /** + * @dev Returns an Ethereum Signed Typed Data, created from a + * `domainSeparator` and a `structHash`. This produces hash corresponding + * to the one signed with the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] + * JSON-RPC method as part of EIP-712. + * + * See {recover}. + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol b/lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol new file mode 100644 index 0000000..eb211a7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, + * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding + * they need in their contracts using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * _Available since v3.4._ + */ +abstract contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to + // invalidate the cached domain separator if the chain id changes. + bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; + uint256 private immutable _CACHED_CHAIN_ID; + address private immutable _CACHED_THIS; + + bytes32 private immutable _HASHED_NAME; + bytes32 private immutable _HASHED_VERSION; + bytes32 private immutable _TYPE_HASH; + + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = block.chainid; + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _CACHED_THIS = address(this); + _TYPE_HASH = typeHash; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol b/lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol new file mode 100644 index 0000000..0ce87fa --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol) + +pragma solidity ^0.8.0; + +/** + * @dev These functions deal with verification of Merkle Tree proofs. + * + * The tree and the proofs can be generated using our + * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. + * You will find a quickstart guide in the readme. + * + * WARNING: You should avoid using leaf values that are 64 bytes long prior to + * hashing, or use a hash function other than keccak256 for hashing leaves. + * This is because the concatenation of a sorted pair of internal nodes in + * the merkle tree could be reinterpreted as a leaf value. + * OpenZeppelin's JavaScript library generates merkle trees that are safe + * against this attack out of the box. + */ +library MerkleProof { + /** + * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree + * defined by `root`. For this, a `proof` must be provided, containing + * sibling hashes on the branch from the leaf to the root of the tree. Each + * pair of leaves and each pair of pre-images are assumed to be sorted. + */ + function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { + return processProof(proof, leaf) == root; + } + + /** + * @dev Calldata version of {verify} + * + * _Available since v4.7._ + */ + function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { + return processProofCalldata(proof, leaf) == root; + } + + /** + * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up + * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt + * hash matches the root of the tree. When processing the proof, the pairs + * of leafs & pre-images are assumed to be sorted. + * + * _Available since v4.4._ + */ + function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { + bytes32 computedHash = leaf; + for (uint256 i = 0; i < proof.length; i++) { + computedHash = _hashPair(computedHash, proof[i]); + } + return computedHash; + } + + /** + * @dev Calldata version of {processProof} + * + * _Available since v4.7._ + */ + function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { + bytes32 computedHash = leaf; + for (uint256 i = 0; i < proof.length; i++) { + computedHash = _hashPair(computedHash, proof[i]); + } + return computedHash; + } + + /** + * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by + * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. + * + * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. + * + * _Available since v4.7._ + */ + function multiProofVerify( + bytes32[] memory proof, + bool[] memory proofFlags, + bytes32 root, + bytes32[] memory leaves + ) internal pure returns (bool) { + return processMultiProof(proof, proofFlags, leaves) == root; + } + + /** + * @dev Calldata version of {multiProofVerify} + * + * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. + * + * _Available since v4.7._ + */ + function multiProofVerifyCalldata( + bytes32[] calldata proof, + bool[] calldata proofFlags, + bytes32 root, + bytes32[] memory leaves + ) internal pure returns (bool) { + return processMultiProofCalldata(proof, proofFlags, leaves) == root; + } + + /** + * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction + * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another + * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false + * respectively. + * + * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree + * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the + * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). + * + * _Available since v4.7._ + */ + function processMultiProof( + bytes32[] memory proof, + bool[] memory proofFlags, + bytes32[] memory leaves + ) internal pure returns (bytes32 merkleRoot) { + // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by + // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the + // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of + // the merkle tree. + uint256 leavesLen = leaves.length; + uint256 totalHashes = proofFlags.length; + + // Check proof validity. + require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); + + // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using + // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". + bytes32[] memory hashes = new bytes32[](totalHashes); + uint256 leafPos = 0; + uint256 hashPos = 0; + uint256 proofPos = 0; + // At each step, we compute the next hash using two values: + // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we + // get the next hash. + // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the + // `proof` array. + for (uint256 i = 0; i < totalHashes; i++) { + bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; + bytes32 b = proofFlags[i] + ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) + : proof[proofPos++]; + hashes[i] = _hashPair(a, b); + } + + if (totalHashes > 0) { + return hashes[totalHashes - 1]; + } else if (leavesLen > 0) { + return leaves[0]; + } else { + return proof[0]; + } + } + + /** + * @dev Calldata version of {processMultiProof}. + * + * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. + * + * _Available since v4.7._ + */ + function processMultiProofCalldata( + bytes32[] calldata proof, + bool[] calldata proofFlags, + bytes32[] memory leaves + ) internal pure returns (bytes32 merkleRoot) { + // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by + // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the + // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of + // the merkle tree. + uint256 leavesLen = leaves.length; + uint256 totalHashes = proofFlags.length; + + // Check proof validity. + require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof"); + + // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using + // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". + bytes32[] memory hashes = new bytes32[](totalHashes); + uint256 leafPos = 0; + uint256 hashPos = 0; + uint256 proofPos = 0; + // At each step, we compute the next hash using two values: + // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we + // get the next hash. + // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the + // `proof` array. + for (uint256 i = 0; i < totalHashes; i++) { + bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; + bytes32 b = proofFlags[i] + ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) + : proof[proofPos++]; + hashes[i] = _hashPair(a, b); + } + + if (totalHashes > 0) { + return hashes[totalHashes - 1]; + } else if (leavesLen > 0) { + return leaves[0]; + } else { + return proof[0]; + } + } + + function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { + return a < b ? _efficientHash(a, b) : _efficientHash(b, a); + } + + function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, a) + mstore(0x20, b) + value := keccak256(0x00, 0x40) + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol b/lib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol new file mode 100644 index 0000000..e06778d --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/SignatureChecker.sol) + +pragma solidity ^0.8.0; + +import "./ECDSA.sol"; +import "../../interfaces/IERC1271.sol"; + +/** + * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA + * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like + * Argent and Gnosis Safe. + * + * _Available since v4.1._ + */ +library SignatureChecker { + /** + * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the + * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. + * + * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus + * change through time. It could return true at block N and false at block N+1 (or the opposite). + */ + function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { + (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); + if (error == ECDSA.RecoverError.NoError && recovered == signer) { + return true; + } + + (bool success, bytes memory result) = signer.staticcall( + abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) + ); + return (success && + result.length == 32 && + abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector)); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/cryptography/draft-EIP712.sol b/lib/openzeppelin-contracts/contracts/utils/cryptography/draft-EIP712.sol new file mode 100644 index 0000000..fdae3ba --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/cryptography/draft-EIP712.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/draft-EIP712.sol) + +pragma solidity ^0.8.0; + +// EIP-712 is Final as of 2022-08-11. This file is deprecated. + +import "./EIP712.sol"; diff --git a/lib/openzeppelin-contracts/contracts/utils/escrow/ConditionalEscrow.sol b/lib/openzeppelin-contracts/contracts/utils/escrow/ConditionalEscrow.sol new file mode 100644 index 0000000..87f5381 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/escrow/ConditionalEscrow.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/escrow/ConditionalEscrow.sol) + +pragma solidity ^0.8.0; + +import "./Escrow.sol"; + +/** + * @title ConditionalEscrow + * @dev Base abstract escrow to only allow withdrawal if a condition is met. + * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. + */ +abstract contract ConditionalEscrow is Escrow { + /** + * @dev Returns whether an address is allowed to withdraw their funds. To be + * implemented by derived contracts. + * @param payee The destination address of the funds. + */ + function withdrawalAllowed(address payee) public view virtual returns (bool); + + function withdraw(address payable payee) public virtual override { + require(withdrawalAllowed(payee), "ConditionalEscrow: payee is not allowed to withdraw"); + super.withdraw(payee); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/escrow/Escrow.sol b/lib/openzeppelin-contracts/contracts/utils/escrow/Escrow.sol new file mode 100644 index 0000000..48dd51a --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/escrow/Escrow.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (utils/escrow/Escrow.sol) + +pragma solidity ^0.8.0; + +import "../../access/Ownable.sol"; +import "../Address.sol"; + +/** + * @title Escrow + * @dev Base escrow contract, holds funds designated for a payee until they + * withdraw them. + * + * Intended usage: This contract (and derived escrow contracts) should be a + * standalone contract, that only interacts with the contract that instantiated + * it. That way, it is guaranteed that all Ether will be handled according to + * the `Escrow` rules, and there is no need to check for payable functions or + * transfers in the inheritance tree. The contract that uses the escrow as its + * payment method should be its owner, and provide public methods redirecting + * to the escrow's deposit and withdraw. + */ +contract Escrow is Ownable { + using Address for address payable; + + event Deposited(address indexed payee, uint256 weiAmount); + event Withdrawn(address indexed payee, uint256 weiAmount); + + mapping(address => uint256) private _deposits; + + function depositsOf(address payee) public view returns (uint256) { + return _deposits[payee]; + } + + /** + * @dev Stores the sent amount as credit to be withdrawn. + * @param payee The destination address of the funds. + * + * Emits a {Deposited} event. + */ + function deposit(address payee) public payable virtual onlyOwner { + uint256 amount = msg.value; + _deposits[payee] += amount; + emit Deposited(payee, amount); + } + + /** + * @dev Withdraw accumulated balance for a payee, forwarding all gas to the + * recipient. + * + * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities. + * Make sure you trust the recipient, or are either following the + * checks-effects-interactions pattern or using {ReentrancyGuard}. + * + * @param payee The address whose funds will be withdrawn and transferred to. + * + * Emits a {Withdrawn} event. + */ + function withdraw(address payable payee) public virtual onlyOwner { + uint256 payment = _deposits[payee]; + + _deposits[payee] = 0; + + payee.sendValue(payment); + + emit Withdrawn(payee, payment); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/escrow/RefundEscrow.sol b/lib/openzeppelin-contracts/contracts/utils/escrow/RefundEscrow.sol new file mode 100644 index 0000000..0e9621f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/escrow/RefundEscrow.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/escrow/RefundEscrow.sol) + +pragma solidity ^0.8.0; + +import "./ConditionalEscrow.sol"; + +/** + * @title RefundEscrow + * @dev Escrow that holds funds for a beneficiary, deposited from multiple + * parties. + * @dev Intended usage: See {Escrow}. Same usage guidelines apply here. + * @dev The owner account (that is, the contract that instantiates this + * contract) may deposit, close the deposit period, and allow for either + * withdrawal by the beneficiary, or refunds to the depositors. All interactions + * with `RefundEscrow` will be made through the owner contract. + */ +contract RefundEscrow is ConditionalEscrow { + using Address for address payable; + + enum State { + Active, + Refunding, + Closed + } + + event RefundsClosed(); + event RefundsEnabled(); + + State private _state; + address payable private immutable _beneficiary; + + /** + * @dev Constructor. + * @param beneficiary_ The beneficiary of the deposits. + */ + constructor(address payable beneficiary_) { + require(beneficiary_ != address(0), "RefundEscrow: beneficiary is the zero address"); + _beneficiary = beneficiary_; + _state = State.Active; + } + + /** + * @return The current state of the escrow. + */ + function state() public view virtual returns (State) { + return _state; + } + + /** + * @return The beneficiary of the escrow. + */ + function beneficiary() public view virtual returns (address payable) { + return _beneficiary; + } + + /** + * @dev Stores funds that may later be refunded. + * @param refundee The address funds will be sent to if a refund occurs. + */ + function deposit(address refundee) public payable virtual override { + require(state() == State.Active, "RefundEscrow: can only deposit while active"); + super.deposit(refundee); + } + + /** + * @dev Allows for the beneficiary to withdraw their funds, rejecting + * further deposits. + */ + function close() public virtual onlyOwner { + require(state() == State.Active, "RefundEscrow: can only close while active"); + _state = State.Closed; + emit RefundsClosed(); + } + + /** + * @dev Allows for refunds to take place, rejecting further deposits. + */ + function enableRefunds() public virtual onlyOwner { + require(state() == State.Active, "RefundEscrow: can only enable refunds while active"); + _state = State.Refunding; + emit RefundsEnabled(); + } + + /** + * @dev Withdraws the beneficiary's funds. + */ + function beneficiaryWithdraw() public virtual { + require(state() == State.Closed, "RefundEscrow: beneficiary can only withdraw while closed"); + beneficiary().sendValue(address(this).balance); + } + + /** + * @dev Returns whether refundees can withdraw their deposits (be refunded). The overridden function receives a + * 'payee' argument, but we ignore it here since the condition is global, not per-payee. + */ + function withdrawalAllowed(address) public view override returns (bool) { + return state() == State.Refunding; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol new file mode 100644 index 0000000..3bf5613 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + * + * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. + */ +abstract contract ERC165 is IERC165 { + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sol new file mode 100644 index 0000000..40ffd68 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/introspection/ERC165Checker.sol) + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +/** + * @dev Library used to query support of an interface declared via {IERC165}. + * + * Note that these functions return the actual result of the query: they do not + * `revert` if an interface is not supported. It is up to the caller to decide + * what to do in these cases. + */ +library ERC165Checker { + // As per the EIP-165 spec, no interface should ever match 0xffffffff + bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff; + + /** + * @dev Returns true if `account` supports the {IERC165} interface. + */ + function supportsERC165(address account) internal view returns (bool) { + // Any contract that implements ERC165 must explicitly indicate support of + // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid + return + supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) && + !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID); + } + + /** + * @dev Returns true if `account` supports the interface defined by + * `interfaceId`. Support for {IERC165} itself is queried automatically. + * + * See {IERC165-supportsInterface}. + */ + function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) { + // query support of both ERC165 as per the spec and support of _interfaceId + return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId); + } + + /** + * @dev Returns a boolean array where each value corresponds to the + * interfaces passed in and whether they're supported or not. This allows + * you to batch check interfaces for a contract where your expectation + * is that some interfaces may not be supported. + * + * See {IERC165-supportsInterface}. + * + * _Available since v3.4._ + */ + function getSupportedInterfaces( + address account, + bytes4[] memory interfaceIds + ) internal view returns (bool[] memory) { + // an array of booleans corresponding to interfaceIds and whether they're supported or not + bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); + + // query support of ERC165 itself + if (supportsERC165(account)) { + // query support of each interface in interfaceIds + for (uint256 i = 0; i < interfaceIds.length; i++) { + interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]); + } + } + + return interfaceIdsSupported; + } + + /** + * @dev Returns true if `account` supports all the interfaces defined in + * `interfaceIds`. Support for {IERC165} itself is queried automatically. + * + * Batch-querying can lead to gas savings by skipping repeated checks for + * {IERC165} support. + * + * See {IERC165-supportsInterface}. + */ + function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) { + // query support of ERC165 itself + if (!supportsERC165(account)) { + return false; + } + + // query support of each interface in interfaceIds + for (uint256 i = 0; i < interfaceIds.length; i++) { + if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) { + return false; + } + } + + // all interfaces supported + return true; + } + + /** + * @notice Query if a contract implements an interface, does not check ERC165 support + * @param account The address of the contract to query for support of an interface + * @param interfaceId The interface identifier, as specified in ERC-165 + * @return true if the contract at account indicates support of the interface with + * identifier interfaceId, false otherwise + * @dev Assumes that account contains a contract that supports ERC165, otherwise + * the behavior of this method is undefined. This precondition can be checked + * with {supportsERC165}. + * Interface identification is specified in ERC-165. + */ + function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) { + // prepare call + bytes memory encodedParams = abi.encodeWithSelector(IERC165.supportsInterface.selector, interfaceId); + + // perform static call + bool success; + uint256 returnSize; + uint256 returnValue; + assembly { + success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20) + returnSize := returndatasize() + returnValue := mload(0x00) + } + + return success && returnSize >= 0x20 && returnValue > 0; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Storage.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Storage.sol new file mode 100644 index 0000000..c99d9f3 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC165Storage.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Storage.sol) + +pragma solidity ^0.8.0; + +import "./ERC165.sol"; + +/** + * @dev Storage based implementation of the {IERC165} interface. + * + * Contracts may inherit from this and call {_registerInterface} to declare + * their support of an interface. + */ +abstract contract ERC165Storage is ERC165 { + /** + * @dev Mapping of interface ids to whether or not it's supported. + */ + mapping(bytes4 => bool) private _supportedInterfaces; + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return super.supportsInterface(interfaceId) || _supportedInterfaces[interfaceId]; + } + + /** + * @dev Registers the contract as an implementer of the interface defined by + * `interfaceId`. Support of the actual ERC165 interface is automatic and + * registering its interface id is not required. + * + * See {IERC165-supportsInterface}. + * + * Requirements: + * + * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). + */ + function _registerInterface(bytes4 interfaceId) internal virtual { + require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); + _supportedInterfaces[interfaceId] = true; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/ERC1820Implementer.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC1820Implementer.sol new file mode 100644 index 0000000..ac5a884 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/ERC1820Implementer.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +import "./IERC1820Implementer.sol"; + +/** + * @dev Implementation of the {IERC1820Implementer} interface. + * + * Contracts may inherit from this and call {_registerInterfaceForAddress} to + * declare their willingness to be implementers. + * {IERC1820Registry-setInterfaceImplementer} should then be called for the + * registration to be complete. + */ +contract ERC1820Implementer is IERC1820Implementer { + bytes32 private constant _ERC1820_ACCEPT_MAGIC = keccak256("ERC1820_ACCEPT_MAGIC"); + + mapping(bytes32 => mapping(address => bool)) private _supportedInterfaces; + + /** + * @dev See {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function canImplementInterfaceForAddress( + bytes32 interfaceHash, + address account + ) public view virtual override returns (bytes32) { + return _supportedInterfaces[interfaceHash][account] ? _ERC1820_ACCEPT_MAGIC : bytes32(0x00); + } + + /** + * @dev Declares the contract as willing to be an implementer of + * `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer} and + * {IERC1820Registry-interfaceHash}. + */ + function _registerInterfaceForAddress(bytes32 interfaceHash, address account) internal virtual { + _supportedInterfaces[interfaceHash][account] = true; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol new file mode 100644 index 0000000..e8cdbdb --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[EIP]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/IERC1820Implementer.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/IERC1820Implementer.sol new file mode 100644 index 0000000..c4d0b30 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/IERC1820Implementer.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC1820Implementer.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface for an ERC1820 implementer, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820#interface-implementation-erc1820implementerinterface[EIP]. + * Used by contracts that will be registered as implementers in the + * {IERC1820Registry}. + */ +interface IERC1820Implementer { + /** + * @dev Returns a special value (`ERC1820_ACCEPT_MAGIC`) if this contract + * implements `interfaceHash` for `account`. + * + * See {IERC1820Registry-setInterfaceImplementer}. + */ + function canImplementInterfaceForAddress(bytes32 interfaceHash, address account) external view returns (bytes32); +} diff --git a/lib/openzeppelin-contracts/contracts/utils/introspection/IERC1820Registry.sol b/lib/openzeppelin-contracts/contracts/utils/introspection/IERC1820Registry.sol new file mode 100644 index 0000000..a146bc2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/introspection/IERC1820Registry.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/introspection/IERC1820Registry.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the global ERC1820 Registry, as defined in the + * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register + * implementers for interfaces in this registry, as well as query support. + * + * Implementers may be shared by multiple accounts, and can also implement more + * than a single interface for each account. Contracts can implement interfaces + * for themselves, but externally-owned accounts (EOA) must delegate this to a + * contract. + * + * {IERC165} interfaces can also be queried via the registry. + * + * For an in-depth explanation and source code analysis, see the EIP text. + */ +interface IERC1820Registry { + event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); + + event ManagerChanged(address indexed account, address indexed newManager); + + /** + * @dev Sets `newManager` as the manager for `account`. A manager of an + * account is able to set interface implementers for it. + * + * By default, each account is its own manager. Passing a value of `0x0` in + * `newManager` will reset the manager to this initial state. + * + * Emits a {ManagerChanged} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + */ + function setManager(address account, address newManager) external; + + /** + * @dev Returns the manager for `account`. + * + * See {setManager}. + */ + function getManager(address account) external view returns (address); + + /** + * @dev Sets the `implementer` contract as ``account``'s implementer for + * `interfaceHash`. + * + * `account` being the zero address is an alias for the caller's address. + * The zero address can also be used in `implementer` to remove an old one. + * + * See {interfaceHash} to learn how these are created. + * + * Emits an {InterfaceImplementerSet} event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not + * end in 28 zeroes). + * - `implementer` must implement {IERC1820Implementer} and return true when + * queried for support, unless `implementer` is the caller. See + * {IERC1820Implementer-canImplementInterfaceForAddress}. + */ + function setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer) external; + + /** + * @dev Returns the implementer of `interfaceHash` for `account`. If no such + * implementer is registered, returns the zero address. + * + * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28 + * zeroes), `account` will be queried for support of it. + * + * `account` being the zero address is an alias for the caller's address. + */ + function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address); + + /** + * @dev Returns the interface hash for an `interfaceName`, as defined in the + * corresponding + * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP]. + */ + function interfaceHash(string calldata interfaceName) external pure returns (bytes32); + + /** + * @notice Updates the cache with whether the contract implements an ERC165 interface or not. + * @param account Address of the contract for which to update the cache. + * @param interfaceId ERC165 interface for which to update the cache. + */ + function updateERC165Cache(address account, bytes4 interfaceId) external; + + /** + * @notice Checks whether a contract implements an ERC165 interface or not. + * If the result is not cached a direct lookup on the contract address is performed. + * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + * {updateERC165Cache} with the contract address. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); + + /** + * @notice Checks whether a contract implements an ERC165 interface or not without using or updating the cache. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account` implements `interfaceId`, false otherwise. + */ + function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); +} diff --git a/lib/openzeppelin-contracts/contracts/utils/math/Math.sol b/lib/openzeppelin-contracts/contracts/utils/math/Math.sol new file mode 100644 index 0000000..f3a83b0 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/math/Math.sol @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + enum Rounding { + Down, // Toward negative infinity + Up, // Toward infinity + Zero // Toward zero + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds up instead + * of rounding down. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b - 1) / b can overflow on addition, so we distribute. + return a == 0 ? 0 : (a - 1) / b + 1; + } + + /** + * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) + * with further edits by Uniswap Labs also under MIT license. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use + // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2^256 + prod0. + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division. + if (prod1 == 0) { + return prod0 / denominator; + } + + // Make sure the result is less than 2^256. Also prevents denominator == 0. + require(denominator > prod1, "Math: mulDiv overflow"); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0]. + uint256 remainder; + assembly { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. + // See https://cs.stackexchange.com/q/138556/92363. + + // Does not overflow because the denominator cannot be zero at this stage in the function. + uint256 twos = denominator & (~denominator + 1); + assembly { + // Divide denominator by twos. + denominator := div(denominator, twos) + + // Divide [prod1 prod0] by twos. + prod0 := div(prod0, twos) + + // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. + twos := add(div(sub(0, twos), twos), 1) + } + + // Shift in bits from prod1 into prod0. + prod0 |= prod1 * twos; + + // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such + // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for + // four bits. That is, denominator * inv = 1 mod 2^4. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works + // in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2^8 + inverse *= 2 - denominator * inverse; // inverse mod 2^16 + inverse *= 2 - denominator * inverse; // inverse mod 2^32 + inverse *= 2 - denominator * inverse; // inverse mod 2^64 + inverse *= 2 - denominator * inverse; // inverse mod 2^128 + inverse *= 2 - denominator * inverse; // inverse mod 2^256 + + // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. + // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is + // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inverse; + return result; + } + } + + /** + * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { + uint256 result = mulDiv(x, y, denominator); + if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { + result += 1; + } + return result; + } + + /** + * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. + * + * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). + */ + function sqrt(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. + // + // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have + // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. + // + // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` + // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` + // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` + // + // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. + uint256 result = 1 << (log2(a) >> 1); + + // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, + // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at + // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision + // into the expected uint128 result. + unchecked { + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + return min(result, a / result); + } + } + + /** + * @notice Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = sqrt(a); + return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); + } + } + + /** + * @dev Return the log in base 2, rounded down, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 128; + } + if (value >> 64 > 0) { + value >>= 64; + result += 64; + } + if (value >> 32 > 0) { + value >>= 32; + result += 32; + } + if (value >> 16 > 0) { + value >>= 16; + result += 16; + } + if (value >> 8 > 0) { + value >>= 8; + result += 8; + } + if (value >> 4 > 0) { + value >>= 4; + result += 4; + } + if (value >> 2 > 0) { + value >>= 2; + result += 2; + } + if (value >> 1 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 2, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log2(value); + return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 10, rounded down, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >= 10 ** 64) { + value /= 10 ** 64; + result += 64; + } + if (value >= 10 ** 32) { + value /= 10 ** 32; + result += 32; + } + if (value >= 10 ** 16) { + value /= 10 ** 16; + result += 16; + } + if (value >= 10 ** 8) { + value /= 10 ** 8; + result += 8; + } + if (value >= 10 ** 4) { + value /= 10 ** 4; + result += 4; + } + if (value >= 10 ** 2) { + value /= 10 ** 2; + result += 2; + } + if (value >= 10 ** 1) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log10(value); + return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 256, rounded down, of a positive value. + * Returns 0 if given 0. + * + * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. + */ + function log256(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 16; + } + if (value >> 64 > 0) { + value >>= 64; + result += 8; + } + if (value >> 32 > 0) { + value >>= 32; + result += 4; + } + if (value >> 16 > 0) { + value >>= 16; + result += 2; + } + if (value >> 8 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log256(value); + return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol b/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol new file mode 100644 index 0000000..435a5f9 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol @@ -0,0 +1,1136 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) +// This file was procedurally generated from scripts/generate/templates/SafeCast.js. + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on `uint256` and `int256` and then downcasting. + */ +library SafeCast { + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v4.7._ + */ + function toUint248(uint256 value) internal pure returns (uint248) { + require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); + return uint248(value); + } + + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v4.7._ + */ + function toUint240(uint256 value) internal pure returns (uint240) { + require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v4.7._ + */ + function toUint232(uint256 value) internal pure returns (uint232) { + require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); + return uint232(value); + } + + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v4.2._ + */ + function toUint224(uint256 value) internal pure returns (uint224) { + require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v4.7._ + */ + function toUint216(uint256 value) internal pure returns (uint216) { + require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v4.7._ + */ + function toUint208(uint256 value) internal pure returns (uint208) { + require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v4.7._ + */ + function toUint200(uint256 value) internal pure returns (uint200) { + require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v4.7._ + */ + function toUint192(uint256 value) internal pure returns (uint192) { + require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v4.7._ + */ + function toUint184(uint256 value) internal pure returns (uint184) { + require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v4.7._ + */ + function toUint176(uint256 value) internal pure returns (uint176) { + require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v4.7._ + */ + function toUint168(uint256 value) internal pure returns (uint168) { + require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v4.7._ + */ + function toUint160(uint256 value) internal pure returns (uint160) { + require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v4.7._ + */ + function toUint152(uint256 value) internal pure returns (uint152) { + require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v4.7._ + */ + function toUint144(uint256 value) internal pure returns (uint144) { + require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v4.7._ + */ + function toUint136(uint256 value) internal pure returns (uint136) { + require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v2.5._ + */ + function toUint128(uint256 value) internal pure returns (uint128) { + require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v4.7._ + */ + function toUint120(uint256 value) internal pure returns (uint120) { + require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v4.7._ + */ + function toUint112(uint256 value) internal pure returns (uint112) { + require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v4.7._ + */ + function toUint104(uint256 value) internal pure returns (uint104) { + require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v4.2._ + */ + function toUint96(uint256 value) internal pure returns (uint96) { + require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v4.7._ + */ + function toUint88(uint256 value) internal pure returns (uint88) { + require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v4.7._ + */ + function toUint80(uint256 value) internal pure returns (uint80) { + require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v4.7._ + */ + function toUint72(uint256 value) internal pure returns (uint72) { + require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v2.5._ + */ + function toUint64(uint256 value) internal pure returns (uint64) { + require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v4.7._ + */ + function toUint56(uint256 value) internal pure returns (uint56) { + require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v4.7._ + */ + function toUint48(uint256 value) internal pure returns (uint48) { + require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v4.7._ + */ + function toUint40(uint256 value) internal pure returns (uint40) { + require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v2.5._ + */ + function toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v4.7._ + */ + function toUint24(uint256 value) internal pure returns (uint24) { + require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v2.5._ + */ + function toUint16(uint256 value) internal pure returns (uint16) { + require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v2.5._ + */ + function toUint8(uint256 value) internal pure returns (uint8) { + require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + * + * _Available since v3.0._ + */ + function toUint256(int256 value) internal pure returns (uint256) { + require(value >= 0, "SafeCast: value must be positive"); + return uint256(value); + } + + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v4.7._ + */ + function toInt248(int256 value) internal pure returns (int248 downcasted) { + downcasted = int248(value); + require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); + } + + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v4.7._ + */ + function toInt240(int256 value) internal pure returns (int240 downcasted) { + downcasted = int240(value); + require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v4.7._ + */ + function toInt232(int256 value) internal pure returns (int232 downcasted) { + downcasted = int232(value); + require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v4.7._ + */ + function toInt224(int256 value) internal pure returns (int224 downcasted) { + downcasted = int224(value); + require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v4.7._ + */ + function toInt216(int256 value) internal pure returns (int216 downcasted) { + downcasted = int216(value); + require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v4.7._ + */ + function toInt208(int256 value) internal pure returns (int208 downcasted) { + downcasted = int208(value); + require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v4.7._ + */ + function toInt200(int256 value) internal pure returns (int200 downcasted) { + downcasted = int200(value); + require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v4.7._ + */ + function toInt192(int256 value) internal pure returns (int192 downcasted) { + downcasted = int192(value); + require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v4.7._ + */ + function toInt184(int256 value) internal pure returns (int184 downcasted) { + downcasted = int184(value); + require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v4.7._ + */ + function toInt176(int256 value) internal pure returns (int176 downcasted) { + downcasted = int176(value); + require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v4.7._ + */ + function toInt168(int256 value) internal pure returns (int168 downcasted) { + downcasted = int168(value); + require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v4.7._ + */ + function toInt160(int256 value) internal pure returns (int160 downcasted) { + downcasted = int160(value); + require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v4.7._ + */ + function toInt152(int256 value) internal pure returns (int152 downcasted) { + downcasted = int152(value); + require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v4.7._ + */ + function toInt144(int256 value) internal pure returns (int144 downcasted) { + downcasted = int144(value); + require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v4.7._ + */ + function toInt136(int256 value) internal pure returns (int136 downcasted) { + downcasted = int136(value); + require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v3.1._ + */ + function toInt128(int256 value) internal pure returns (int128 downcasted) { + downcasted = int128(value); + require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v4.7._ + */ + function toInt120(int256 value) internal pure returns (int120 downcasted) { + downcasted = int120(value); + require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v4.7._ + */ + function toInt112(int256 value) internal pure returns (int112 downcasted) { + downcasted = int112(value); + require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v4.7._ + */ + function toInt104(int256 value) internal pure returns (int104 downcasted) { + downcasted = int104(value); + require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v4.7._ + */ + function toInt96(int256 value) internal pure returns (int96 downcasted) { + downcasted = int96(value); + require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v4.7._ + */ + function toInt88(int256 value) internal pure returns (int88 downcasted) { + downcasted = int88(value); + require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v4.7._ + */ + function toInt80(int256 value) internal pure returns (int80 downcasted) { + downcasted = int80(value); + require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v4.7._ + */ + function toInt72(int256 value) internal pure returns (int72 downcasted) { + downcasted = int72(value); + require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v3.1._ + */ + function toInt64(int256 value) internal pure returns (int64 downcasted) { + downcasted = int64(value); + require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v4.7._ + */ + function toInt56(int256 value) internal pure returns (int56 downcasted) { + downcasted = int56(value); + require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v4.7._ + */ + function toInt48(int256 value) internal pure returns (int48 downcasted) { + downcasted = int48(value); + require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v4.7._ + */ + function toInt40(int256 value) internal pure returns (int40 downcasted) { + downcasted = int40(value); + require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v3.1._ + */ + function toInt32(int256 value) internal pure returns (int32 downcasted) { + downcasted = int32(value); + require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v4.7._ + */ + function toInt24(int256 value) internal pure returns (int24 downcasted) { + downcasted = int24(value); + require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v3.1._ + */ + function toInt16(int256 value) internal pure returns (int16 downcasted) { + downcasted = int16(value); + require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v3.1._ + */ + function toInt8(int256 value) internal pure returns (int8 downcasted) { + downcasted = int8(value); + require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + * + * _Available since v3.0._ + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); + return int256(value); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol b/lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol new file mode 100644 index 0000000..2f48fb7 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) + +pragma solidity ^0.8.0; + +// CAUTION +// This version of SafeMath should only be used with Solidity 0.8 or later, +// because it relies on the compiler's built in overflow checks. + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler + * now has built in overflow checking. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the subtraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return a - b; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + return a * b; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return a % b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + unchecked { + require(b <= a, errorMessage); + return a - b; + } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a / b; + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a % b; + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol b/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol new file mode 100644 index 0000000..3ea9f8b --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Standard signed math utilities missing in the Solidity language. + */ +library SignedMath { + /** + * @dev Returns the largest of two signed numbers. + */ + function max(int256 a, int256 b) internal pure returns (int256) { + return a > b ? a : b; + } + + /** + * @dev Returns the smallest of two signed numbers. + */ + function min(int256 a, int256 b) internal pure returns (int256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two signed numbers without overflow. + * The result is rounded towards zero. + */ + function average(int256 a, int256 b) internal pure returns (int256) { + // Formula from the book "Hacker's Delight" + int256 x = (a & b) + ((a ^ b) >> 1); + return x + (int256(uint256(x) >> 255) & (a ^ b)); + } + + /** + * @dev Returns the absolute unsigned value of a signed value. + */ + function abs(int256 n) internal pure returns (uint256) { + unchecked { + // must be unchecked in order to support `n = type(int256).min` + return uint256(n >= 0 ? n : -n); + } + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/math/SignedSafeMath.sol b/lib/openzeppelin-contracts/contracts/utils/math/SignedSafeMath.sol new file mode 100644 index 0000000..6704d4c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/math/SignedSafeMath.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (utils/math/SignedSafeMath.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler + * now has built in overflow checking. + */ +library SignedSafeMath { + /** + * @dev Returns the multiplication of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(int256 a, int256 b) internal pure returns (int256) { + return a * b; + } + + /** + * @dev Returns the integer division of two signed integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(int256 a, int256 b) internal pure returns (int256) { + return a / b; + } + + /** + * @dev Returns the subtraction of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(int256 a, int256 b) internal pure returns (int256) { + return a - b; + } + + /** + * @dev Returns the addition of two signed integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(int256 a, int256 b) internal pure returns (int256) { + return a + b; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/structs/BitMaps.sol b/lib/openzeppelin-contracts/contracts/utils/structs/BitMaps.sol new file mode 100644 index 0000000..eb67bfa --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/structs/BitMaps.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/BitMaps.sol) +pragma solidity ^0.8.0; + +/** + * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. + * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor]. + */ +library BitMaps { + struct BitMap { + mapping(uint256 => uint256) _data; + } + + /** + * @dev Returns whether the bit at `index` is set. + */ + function get(BitMap storage bitmap, uint256 index) internal view returns (bool) { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + return bitmap._data[bucket] & mask != 0; + } + + /** + * @dev Sets the bit at `index` to the boolean `value`. + */ + function setTo(BitMap storage bitmap, uint256 index, bool value) internal { + if (value) { + set(bitmap, index); + } else { + unset(bitmap, index); + } + } + + /** + * @dev Sets the bit at `index`. + */ + function set(BitMap storage bitmap, uint256 index) internal { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + bitmap._data[bucket] |= mask; + } + + /** + * @dev Unsets the bit at `index`. + */ + function unset(BitMap storage bitmap, uint256 index) internal { + uint256 bucket = index >> 8; + uint256 mask = 1 << (index & 0xff); + bitmap._data[bucket] &= ~mask; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/structs/DoubleEndedQueue.sol b/lib/openzeppelin-contracts/contracts/utils/structs/DoubleEndedQueue.sol new file mode 100644 index 0000000..eae79ad --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/structs/DoubleEndedQueue.sol @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (utils/structs/DoubleEndedQueue.sol) +pragma solidity ^0.8.4; + +import "../math/SafeCast.sol"; + +/** + * @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of + * the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and + * FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that + * the existing queue contents are left in storage. + * + * The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be + * used in storage, and not in memory. + * ``` + * DoubleEndedQueue.Bytes32Deque queue; + * ``` + * + * _Available since v4.6._ + */ +library DoubleEndedQueue { + /** + * @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty. + */ + error Empty(); + + /** + * @dev An operation (e.g. {at}) couldn't be completed due to an index being out of bounds. + */ + error OutOfBounds(); + + /** + * @dev Indices are signed integers because the queue can grow in any direction. They are 128 bits so begin and end + * are packed in a single storage slot for efficient access. Since the items are added one at a time we can safely + * assume that these 128-bit indices will not overflow, and use unchecked arithmetic. + * + * Struct members have an underscore prefix indicating that they are "private" and should not be read or written to + * directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and + * lead to unexpected behavior. + * + * Indices are in the range [begin, end) which means the first item is at data[begin] and the last item is at + * data[end - 1]. + */ + struct Bytes32Deque { + int128 _begin; + int128 _end; + mapping(int128 => bytes32) _data; + } + + /** + * @dev Inserts an item at the end of the queue. + */ + function pushBack(Bytes32Deque storage deque, bytes32 value) internal { + int128 backIndex = deque._end; + deque._data[backIndex] = value; + unchecked { + deque._end = backIndex + 1; + } + } + + /** + * @dev Removes the item at the end of the queue and returns it. + * + * Reverts with `Empty` if the queue is empty. + */ + function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 backIndex; + unchecked { + backIndex = deque._end - 1; + } + value = deque._data[backIndex]; + delete deque._data[backIndex]; + deque._end = backIndex; + } + + /** + * @dev Inserts an item at the beginning of the queue. + */ + function pushFront(Bytes32Deque storage deque, bytes32 value) internal { + int128 frontIndex; + unchecked { + frontIndex = deque._begin - 1; + } + deque._data[frontIndex] = value; + deque._begin = frontIndex; + } + + /** + * @dev Removes the item at the beginning of the queue and returns it. + * + * Reverts with `Empty` if the queue is empty. + */ + function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 frontIndex = deque._begin; + value = deque._data[frontIndex]; + delete deque._data[frontIndex]; + unchecked { + deque._begin = frontIndex + 1; + } + } + + /** + * @dev Returns the item at the beginning of the queue. + * + * Reverts with `Empty` if the queue is empty. + */ + function front(Bytes32Deque storage deque) internal view returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 frontIndex = deque._begin; + return deque._data[frontIndex]; + } + + /** + * @dev Returns the item at the end of the queue. + * + * Reverts with `Empty` if the queue is empty. + */ + function back(Bytes32Deque storage deque) internal view returns (bytes32 value) { + if (empty(deque)) revert Empty(); + int128 backIndex; + unchecked { + backIndex = deque._end - 1; + } + return deque._data[backIndex]; + } + + /** + * @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at + * `length(deque) - 1`. + * + * Reverts with `OutOfBounds` if the index is out of bounds. + */ + function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) { + // int256(deque._begin) is a safe upcast + int128 idx = SafeCast.toInt128(int256(deque._begin) + SafeCast.toInt256(index)); + if (idx >= deque._end) revert OutOfBounds(); + return deque._data[idx]; + } + + /** + * @dev Resets the queue back to being empty. + * + * NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses + * out on potential gas refunds. + */ + function clear(Bytes32Deque storage deque) internal { + deque._begin = 0; + deque._end = 0; + } + + /** + * @dev Returns the number of items in the queue. + */ + function length(Bytes32Deque storage deque) internal view returns (uint256) { + // The interface preserves the invariant that begin <= end so we assume this will not overflow. + // We also assume there are at most int256.max items in the queue. + unchecked { + return uint256(int256(deque._end) - int256(deque._begin)); + } + } + + /** + * @dev Returns true if the queue is empty. + */ + function empty(Bytes32Deque storage deque) internal view returns (bool) { + return deque._end <= deque._begin; + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sol b/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sol new file mode 100644 index 0000000..d359671 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sol @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol) +// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js. + +pragma solidity ^0.8.0; + +import "./EnumerableSet.sol"; + +/** + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableMap for EnumerableMap.UintToAddressMap; + * + * // Declare a set state variable + * EnumerableMap.UintToAddressMap private myMap; + * } + * ``` + * + * The following map types are supported: + * + * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0 + * - `address -> uint256` (`AddressToUintMap`) since v4.6.0 + * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0 + * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0 + * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0 + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableMap. + * ==== + */ +library EnumerableMap { + using EnumerableSet for EnumerableSet.Bytes32Set; + + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Map type with + // bytes32 keys and values. + // The Map implementation uses private functions, and user-facing + // implementations (such as Uint256ToAddressMap) are just wrappers around + // the underlying Map. + // This means that we can only create new EnumerableMaps for types that fit + // in bytes32. + + struct Bytes32ToBytes32Map { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 => bytes32) _values; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) { + map._values[key] = value; + return map._keys.add(key); + } + + /** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) { + delete map._values[key]; + return map._keys.remove(key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) { + return map._keys.contains(key); + } + + /** + * @dev Returns the number of key-value pairs in the map. O(1). + */ + function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) { + return map._keys.length(); + } + + /** + * @dev Returns the key-value pair stored at position `index` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) { + bytes32 key = map._keys.at(index); + return (key, map._values[key]); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) { + bytes32 value = map._values[key]; + if (value == bytes32(0)) { + return (contains(map, key), bytes32(0)); + } else { + return (true, value); + } + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key"); + return value; + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + Bytes32ToBytes32Map storage map, + bytes32 key, + string memory errorMessage + ) internal view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || contains(map, key), errorMessage); + return value; + } + + // UintToUintMap + + struct UintToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) { + return set(map._inner, bytes32(key), bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToUintMap storage map, uint256 key) internal returns (bool) { + return remove(map._inner, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) { + return contains(map._inner, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToUintMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (uint256(key), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, bytes32(key)); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(key))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get(UintToUintMap storage map, uint256 key, string memory errorMessage) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(key), errorMessage)); + } + + // UintToAddressMap + + struct UintToAddressMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) { + return set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { + return remove(map._inner, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { + return contains(map._inner, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToAddressMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (uint256(key), address(uint160(uint256(value)))); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { + (bool success, bytes32 value) = tryGet(map._inner, bytes32(key)); + return (success, address(uint160(uint256(value)))); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { + return address(uint160(uint256(get(map._inner, bytes32(key))))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + UintToAddressMap storage map, + uint256 key, + string memory errorMessage + ) internal view returns (address) { + return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage)))); + } + + // AddressToUintMap + + struct AddressToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) { + return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(AddressToUintMap storage map, address key) internal returns (bool) { + return remove(map._inner, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(AddressToUintMap storage map, address key) internal view returns (bool) { + return contains(map._inner, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(AddressToUintMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (address(uint160(uint256(key))), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key)))); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(AddressToUintMap storage map, address key) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(uint256(uint160(key))))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + AddressToUintMap storage map, + address key, + string memory errorMessage + ) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage)); + } + + // Bytes32ToUintMap + + struct Bytes32ToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) { + return set(map._inner, key, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) { + return remove(map._inner, key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) { + return contains(map._inner, key); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(Bytes32ToUintMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (key, uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, key); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) { + return uint256(get(map._inner, key)); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + Bytes32ToUintMap storage map, + bytes32 key, + string memory errorMessage + ) internal view returns (uint256) { + return uint256(get(map._inner, key, errorMessage)); + } +} diff --git a/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol b/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol new file mode 100644 index 0000000..4c701c2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) +// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. + +pragma solidity ^0.8.0; + +/** + * @dev Library for managing + * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive + * types. + * + * Sets have the following properties: + * + * - Elements are added, removed, and checked for existence in constant time + * (O(1)). + * - Elements are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableSet for EnumerableSet.AddressSet; + * + * // Declare a set state variable + * EnumerableSet.AddressSet private mySet; + * } + * ``` + * + * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) + * and `uint256` (`UintSet`) are supported. + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableSet. + * ==== + */ +library EnumerableSet { + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Set type with + // bytes32 values. + // The Set implementation uses private functions, and user-facing + // implementations (such as AddressSet) are just wrappers around the + // underlying Set. + // This means that we can only create new EnumerableSets for types that fit + // in bytes32. + + struct Set { + // Storage of set values + bytes32[] _values; + // Position of the value in the `values` array, plus 1 because index 0 + // means a value is not in the set. + mapping(bytes32 => uint256) _indexes; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function _add(Set storage set, bytes32 value) private returns (bool) { + if (!_contains(set, value)) { + set._values.push(value); + // The value is stored at length-1, but we add 1 to all indexes + // and use 0 as a sentinel value + set._indexes[value] = set._values.length; + return true; + } else { + return false; + } + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function _remove(Set storage set, bytes32 value) private returns (bool) { + // We read and store the value's index to prevent multiple reads from the same storage slot + uint256 valueIndex = set._indexes[value]; + + if (valueIndex != 0) { + // Equivalent to contains(set, value) + // To delete an element from the _values array in O(1), we swap the element to delete with the last one in + // the array, and then remove the last element (sometimes called as 'swap and pop'). + // This modifies the order of the array, as noted in {at}. + + uint256 toDeleteIndex = valueIndex - 1; + uint256 lastIndex = set._values.length - 1; + + if (lastIndex != toDeleteIndex) { + bytes32 lastValue = set._values[lastIndex]; + + // Move the last value to the index where the value to delete is + set._values[toDeleteIndex] = lastValue; + // Update the index for the moved value + set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex + } + + // Delete the slot where the moved value was stored + set._values.pop(); + + // Delete the index for the deleted slot + delete set._indexes[value]; + + return true; + } else { + return false; + } + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function _contains(Set storage set, bytes32 value) private view returns (bool) { + return set._indexes[value] != 0; + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function _length(Set storage set) private view returns (uint256) { + return set._values.length; + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Set storage set, uint256 index) private view returns (bytes32) { + return set._values[index]; + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function _values(Set storage set) private view returns (bytes32[] memory) { + return set._values; + } + + // Bytes32Set + + struct Bytes32Set { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _add(set._inner, value); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { + return _remove(set._inner, value); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { + return _contains(set._inner, value); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(Bytes32Set storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { + return _at(set._inner, index); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { + bytes32[] memory store = _values(set._inner); + bytes32[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // AddressSet + + struct AddressSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(AddressSet storage set, address value) internal returns (bool) { + return _add(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(AddressSet storage set, address value) internal returns (bool) { + return _remove(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(AddressSet storage set, address value) internal view returns (bool) { + return _contains(set._inner, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(AddressSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressSet storage set, uint256 index) internal view returns (address) { + return address(uint160(uint256(_at(set._inner, index)))); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(AddressSet storage set) internal view returns (address[] memory) { + bytes32[] memory store = _values(set._inner); + address[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // UintSet + + struct UintSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(UintSet storage set, uint256 value) internal returns (bool) { + return _add(set._inner, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(UintSet storage set, uint256 value) internal returns (bool) { + return _remove(set._inner, bytes32(value)); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(UintSet storage set, uint256 value) internal view returns (bool) { + return _contains(set._inner, bytes32(value)); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(UintSet storage set) internal view returns (uint256) { + return _length(set._inner); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintSet storage set, uint256 index) internal view returns (uint256) { + return uint256(_at(set._inner, index)); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(UintSet storage set) internal view returns (uint256[] memory) { + bytes32[] memory store = _values(set._inner); + uint256[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/amb/IAMB.sol b/lib/openzeppelin-contracts/contracts/vendor/amb/IAMB.sol new file mode 100644 index 0000000..73a2bd2 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/amb/IAMB.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (vendor/amb/IAMB.sol) +pragma solidity ^0.8.0; + +interface IAMB { + event UserRequestForAffirmation(bytes32 indexed messageId, bytes encodedData); + event UserRequestForSignature(bytes32 indexed messageId, bytes encodedData); + event AffirmationCompleted( + address indexed sender, + address indexed executor, + bytes32 indexed messageId, + bool status + ); + event RelayedMessage(address indexed sender, address indexed executor, bytes32 indexed messageId, bool status); + + function messageSender() external view returns (address); + + function maxGasPerTx() external view returns (uint256); + + function transactionHash() external view returns (bytes32); + + function messageId() external view returns (bytes32); + + function messageSourceChainId() external view returns (bytes32); + + function messageCallStatus(bytes32 _messageId) external view returns (bool); + + function failedMessageDataHash(bytes32 _messageId) external view returns (bytes32); + + function failedMessageReceiver(bytes32 _messageId) external view returns (address); + + function failedMessageSender(bytes32 _messageId) external view returns (address); + + function requireToPassMessage(address _contract, bytes calldata _data, uint256 _gas) external returns (bytes32); + + function requireToConfirmMessage(address _contract, bytes calldata _data, uint256 _gas) external returns (bytes32); + + function sourceChainId() external view returns (uint256); + + function destinationChainId() external view returns (uint256); +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IArbSys.sol b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IArbSys.sol new file mode 100644 index 0000000..9b79d5c --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IArbSys.sol @@ -0,0 +1,134 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 +// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IArbSys.sol) + +pragma solidity >=0.4.21 <0.9.0; + +/** + * @title System level functionality + * @notice For use by contracts to interact with core L2-specific functionality. + * Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. + */ +interface IArbSys { + /** + * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) + * @return block number as int + */ + function arbBlockNumber() external view returns (uint256); + + /** + * @notice Get Arbitrum block hash (reverts unless currentBlockNum-256 <= arbBlockNum < currentBlockNum) + * @return block hash + */ + function arbBlockHash(uint256 arbBlockNum) external view returns (bytes32); + + /** + * @notice Gets the rollup's unique chain identifier + * @return Chain identifier as int + */ + function arbChainID() external view returns (uint256); + + /** + * @notice Get internal version number identifying an ArbOS build + * @return version number as int + */ + function arbOSVersion() external view returns (uint256); + + /** + * @notice Returns 0 since Nitro has no concept of storage gas + * @return uint 0 + */ + function getStorageGasAvailable() external view returns (uint256); + + /** + * @notice (deprecated) check if current call is top level (meaning it was triggered by an EoA or a L1 contract) + * @dev this call has been deprecated and may be removed in a future release + * @return true if current execution frame is not a call by another L2 contract + */ + function isTopLevelCall() external view returns (bool); + + /** + * @notice map L1 sender contract address to its L2 alias + * @param sender sender address + * @param unused argument no longer used + * @return aliased sender address + */ + function mapL1SenderContractAddressToL2Alias(address sender, address unused) external pure returns (address); + + /** + * @notice check if the caller (of this caller of this) is an aliased L1 contract address + * @return true iff the caller's address is an alias for an L1 contract address + */ + function wasMyCallersAddressAliased() external view returns (bool); + + /** + * @notice return the address of the caller (of this caller of this), without applying L1 contract address aliasing + * @return address of the caller's caller, without applying L1 contract address aliasing + */ + function myCallersAddressWithoutAliasing() external view returns (address); + + /** + * @notice Send given amount of Eth to dest from sender. + * This is a convenience function, which is equivalent to calling sendTxToL1 with empty data. + * @param destination recipient address on L1 + * @return unique identifier for this L2-to-L1 transaction. + */ + function withdrawEth(address destination) external payable returns (uint256); + + /** + * @notice Send a transaction to L1 + * @dev it is not possible to execute on the L1 any L2-to-L1 transaction which contains data + * to a contract address without any code (as enforced by the Bridge contract). + * @param destination recipient address on L1 + * @param data (optional) calldata for L1 contract call + * @return a unique identifier for this L2-to-L1 transaction. + */ + function sendTxToL1(address destination, bytes calldata data) external payable returns (uint256); + + /** + * @notice Get send Merkle tree state + * @return size number of sends in the history + * @return root root hash of the send history + * @return partials hashes of partial subtrees in the send history tree + */ + function sendMerkleTreeState() external view returns (uint256 size, bytes32 root, bytes32[] memory partials); + + /** + * @notice creates a send txn from L2 to L1 + * @param position = (level << 192) + leaf = (0 << 192) + leaf = leaf + */ + event L2ToL1Tx( + address caller, + address indexed destination, + uint256 indexed hash, + uint256 indexed position, + uint256 arbBlockNum, + uint256 ethBlockNum, + uint256 timestamp, + uint256 callvalue, + bytes data + ); + + /// @dev DEPRECATED in favour of the new L2ToL1Tx event above after the nitro upgrade + event L2ToL1Transaction( + address caller, + address indexed destination, + uint256 indexed uniqueId, + uint256 indexed batchNumber, + uint256 indexInBatch, + uint256 arbBlockNum, + uint256 ethBlockNum, + uint256 timestamp, + uint256 callvalue, + bytes data + ); + + /** + * @notice logs a merkle branch for proof synthesis + * @param reserved an index meant only to align the 4th index with L2ToL1Transaction's 4th event + * @param hash the merkle hash + * @param position = (level << 192) + leaf + */ + event SendMerkleUpdate(uint256 indexed reserved, bytes32 indexed hash, uint256 indexed position); +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IBridge.sol b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IBridge.sol new file mode 100644 index 0000000..e71bedc --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IBridge.sol @@ -0,0 +1,102 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 +// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IBridge.sol) + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +interface IBridge { + event MessageDelivered( + uint256 indexed messageIndex, + bytes32 indexed beforeInboxAcc, + address inbox, + uint8 kind, + address sender, + bytes32 messageDataHash, + uint256 baseFeeL1, + uint64 timestamp + ); + + event BridgeCallTriggered(address indexed outbox, address indexed to, uint256 value, bytes data); + + event InboxToggle(address indexed inbox, bool enabled); + + event OutboxToggle(address indexed outbox, bool enabled); + + event SequencerInboxUpdated(address newSequencerInbox); + + function allowedDelayedInboxList(uint256) external returns (address); + + function allowedOutboxList(uint256) external returns (address); + + /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. + function delayedInboxAccs(uint256) external view returns (bytes32); + + /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. + function sequencerInboxAccs(uint256) external view returns (bytes32); + + // OpenZeppelin: changed return type from IOwnable + function rollup() external view returns (address); + + function sequencerInbox() external view returns (address); + + function activeOutbox() external view returns (address); + + function allowedDelayedInboxes(address inbox) external view returns (bool); + + function allowedOutboxes(address outbox) external view returns (bool); + + function sequencerReportedSubMessageCount() external view returns (uint256); + + /** + * @dev Enqueue a message in the delayed inbox accumulator. + * These messages are later sequenced in the SequencerInbox, either + * by the sequencer as part of a normal batch, or by force inclusion. + */ + function enqueueDelayedMessage( + uint8 kind, + address sender, + bytes32 messageDataHash + ) external payable returns (uint256); + + function executeCall( + address to, + uint256 value, + bytes calldata data + ) external returns (bool success, bytes memory returnData); + + function delayedMessageCount() external view returns (uint256); + + function sequencerMessageCount() external view returns (uint256); + + // ---------- onlySequencerInbox functions ---------- + + function enqueueSequencerMessage( + bytes32 dataHash, + uint256 afterDelayedMessagesRead, + uint256 prevMessageCount, + uint256 newMessageCount + ) external returns (uint256 seqMessageIndex, bytes32 beforeAcc, bytes32 delayedAcc, bytes32 acc); + + /** + * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type + * This is done through a separate function entrypoint instead of allowing the sequencer inbox + * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either + * every delayed inbox or every sequencer inbox call. + */ + function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) external returns (uint256 msgNum); + + // ---------- onlyRollupOrOwner functions ---------- + + function setSequencerInbox(address _sequencerInbox) external; + + function setDelayedInbox(address inbox, bool enabled) external; + + function setOutbox(address inbox, bool enabled) external; + + // ---------- initializer ---------- + + // OpenZeppelin: changed rollup_ type from IOwnable + function initialize(address rollup_) external; +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IDelayedMessageProvider.sol b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IDelayedMessageProvider.sol new file mode 100644 index 0000000..914c25f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IDelayedMessageProvider.sol @@ -0,0 +1,16 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 +// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IDelayedMessageProvider.sol) + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +interface IDelayedMessageProvider { + /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator + event InboxMessageDelivered(uint256 indexed messageNum, bytes data); + + /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator + /// same as InboxMessageDelivered but the batch data is available in tx.input + event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IInbox.sol b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IInbox.sol new file mode 100644 index 0000000..a8b6751 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IInbox.sol @@ -0,0 +1,152 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 +// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IInbox.sol) + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +import "./IBridge.sol"; +import "./IDelayedMessageProvider.sol"; + +interface IInbox is IDelayedMessageProvider { + function bridge() external view returns (IBridge); + + // OpenZeppelin: changed return type from ISequencerInbox + function sequencerInbox() external view returns (address); + + /** + * @notice Send a generic L2 message to the chain + * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input + * @param messageData Data of the message being sent + */ + function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); + + /** + * @notice Send a generic L2 message to the chain + * @dev This method can be used to send any type of message that doesn't require L1 validation + * @param messageData Data of the message being sent + */ + function sendL2Message(bytes calldata messageData) external returns (uint256); + + function sendL1FundedUnsignedTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + address to, + bytes calldata data + ) external payable returns (uint256); + + function sendL1FundedContractTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + address to, + bytes calldata data + ) external payable returns (uint256); + + function sendUnsignedTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + uint256 nonce, + address to, + uint256 value, + bytes calldata data + ) external returns (uint256); + + function sendContractTransaction( + uint256 gasLimit, + uint256 maxFeePerGas, + address to, + uint256 value, + bytes calldata data + ) external returns (uint256); + + /** + * @notice Get the L1 fee for submitting a retryable + * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value + * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! + * @param dataLength The length of the retryable's calldata, in bytes + * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used + */ + function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) external view returns (uint256); + + /** + * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract + * @dev This does not trigger the fallback function when receiving in the L2 side. + * Look into retryable tickets if you are interested in this functionality. + * @dev This function should not be called inside contract constructors + */ + function depositEth() external payable returns (uint256); + + /** + * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts + * @dev all msg.value will deposited to callValueRefundAddress on L2 + * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error + * @param to destination L2 contract address + * @param l2CallValue call value for retryable L2 message + * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee + * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance + * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled + * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param data ABI encoded data of L2 message + * @return unique message number of the retryable transaction + */ + function createRetryableTicket( + address to, + uint256 l2CallValue, + uint256 maxSubmissionCost, + address excessFeeRefundAddress, + address callValueRefundAddress, + uint256 gasLimit, + uint256 maxFeePerGas, + bytes calldata data + ) external payable returns (uint256); + + /** + * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts + * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds + * come from the deposit alone, rather than falling back on the user's L2 balance + * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). + * createRetryableTicket method is the recommended standard. + * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error + * @param to destination L2 contract address + * @param l2CallValue call value for retryable L2 message + * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee + * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance + * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled + * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) + * @param data ABI encoded data of L2 message + * @return unique message number of the retryable transaction + */ + function unsafeCreateRetryableTicket( + address to, + uint256 l2CallValue, + uint256 maxSubmissionCost, + address excessFeeRefundAddress, + address callValueRefundAddress, + uint256 gasLimit, + uint256 maxFeePerGas, + bytes calldata data + ) external payable returns (uint256); + + // ---------- onlyRollupOrOwner functions ---------- + + /// @notice pauses all inbox functionality + function pause() external; + + /// @notice unpauses all inbox functionality + function unpause() external; + + // ---------- initializer ---------- + + /** + * @dev function to be called one time during the inbox upgrade process + * this is used to fix the storage slots + */ + function postUpgradeInit(IBridge _bridge) external; + + // OpenZeppelin: changed _sequencerInbox type from ISequencerInbox + function initialize(IBridge _bridge, address _sequencerInbox) external; +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IOutbox.sol b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IOutbox.sol new file mode 100644 index 0000000..22fa58f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/arbitrum/IOutbox.sol @@ -0,0 +1,117 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE +// SPDX-License-Identifier: BUSL-1.1 +// OpenZeppelin Contracts (last updated v4.8.0) (vendor/arbitrum/IOutbox.sol) + +// solhint-disable-next-line compiler-version +pragma solidity >=0.6.9 <0.9.0; + +import "./IBridge.sol"; + +interface IOutbox { + event SendRootUpdated(bytes32 indexed blockHash, bytes32 indexed outputRoot); + event OutBoxTransactionExecuted( + address indexed to, + address indexed l2Sender, + uint256 indexed zero, + uint256 transactionIndex + ); + + function rollup() external view returns (address); // the rollup contract + + function bridge() external view returns (IBridge); // the bridge contract + + function spent(uint256) external view returns (bytes32); // packed spent bitmap + + function roots(bytes32) external view returns (bytes32); // maps root hashes => L2 block hash + + // solhint-disable-next-line func-name-mixedcase + function OUTBOX_VERSION() external view returns (uint128); // the outbox version + + function updateSendRoot(bytes32 sendRoot, bytes32 l2BlockHash) external; + + /// @notice When l2ToL1Sender returns a nonzero address, the message was originated by an L2 account + /// When the return value is zero, that means this is a system message + /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies + function l2ToL1Sender() external view returns (address); + + /// @return l2Block return L2 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active + function l2ToL1Block() external view returns (uint256); + + /// @return l1Block return L1 block when the L2 tx was initiated or 0 if no L2 to L1 transaction is active + function l2ToL1EthBlock() external view returns (uint256); + + /// @return timestamp return L2 timestamp when the L2 tx was initiated or 0 if no L2 to L1 transaction is active + function l2ToL1Timestamp() external view returns (uint256); + + /// @return outputId returns the unique output identifier of the L2 to L1 tx or 0 if no L2 to L1 transaction is active + function l2ToL1OutputId() external view returns (bytes32); + + /** + * @notice Executes a messages in an Outbox entry. + * @dev Reverts if dispute period hasn't expired, since the outbox entry + * is only created once the rollup confirms the respective assertion. + * @dev it is not possible to execute any L2-to-L1 transaction which contains data + * to a contract address without any code (as enforced by the Bridge contract). + * @param proof Merkle proof of message inclusion in send root + * @param index Merkle path to message + * @param l2Sender sender if original message (i.e., caller of ArbSys.sendTxToL1) + * @param to destination address for L1 contract call + * @param l2Block l2 block number at which sendTxToL1 call was made + * @param l1Block l1 block number at which sendTxToL1 call was made + * @param l2Timestamp l2 Timestamp at which sendTxToL1 call was made + * @param value wei in L1 message + * @param data abi-encoded L1 message data + */ + function executeTransaction( + bytes32[] calldata proof, + uint256 index, + address l2Sender, + address to, + uint256 l2Block, + uint256 l1Block, + uint256 l2Timestamp, + uint256 value, + bytes calldata data + ) external; + + /** + * @dev function used to simulate the result of a particular function call from the outbox + * it is useful for things such as gas estimates. This function includes all costs except for + * proof validation (which can be considered offchain as a somewhat of a fixed cost - it's + * not really a fixed cost, but can be treated as so with a fixed overhead for gas estimation). + * We can't include the cost of proof validation since this is intended to be used to simulate txs + * that are included in yet-to-be confirmed merkle roots. The simulation entrypoint could instead pretend + * to confirm a pending merkle root, but that would be less practical for integrating with tooling. + * It is only possible to trigger it when the msg sender is address zero, which should be impossible + * unless under simulation in an eth_call or eth_estimateGas + */ + function executeTransactionSimulation( + uint256 index, + address l2Sender, + address to, + uint256 l2Block, + uint256 l1Block, + uint256 l2Timestamp, + uint256 value, + bytes calldata data + ) external; + + /** + * @param index Merkle path to message + * @return true if the message has been spent + */ + function isSpent(uint256 index) external view returns (bool); + + function calculateItemHash( + address l2Sender, + address to, + uint256 l2Block, + uint256 l1Block, + uint256 l2Timestamp, + uint256 value, + bytes calldata data + ) external pure returns (bytes32); + + function calculateMerkleRoot(bytes32[] memory proof, uint256 path, bytes32 item) external pure returns (bytes32); +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/compound/ICompoundTimelock.sol b/lib/openzeppelin-contracts/contracts/vendor/compound/ICompoundTimelock.sol new file mode 100644 index 0000000..fb33a68 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/compound/ICompoundTimelock.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (vendor/compound/ICompoundTimelock.sol) + +pragma solidity ^0.8.0; + +/** + * https://github.com/compound-finance/compound-protocol/blob/master/contracts/Timelock.sol[Compound's timelock] interface + */ +interface ICompoundTimelock { + event NewAdmin(address indexed newAdmin); + event NewPendingAdmin(address indexed newPendingAdmin); + event NewDelay(uint256 indexed newDelay); + event CancelTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event ExecuteTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + event QueueTransaction( + bytes32 indexed txHash, + address indexed target, + uint256 value, + string signature, + bytes data, + uint256 eta + ); + + receive() external payable; + + // solhint-disable-next-line func-name-mixedcase + function GRACE_PERIOD() external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function MINIMUM_DELAY() external view returns (uint256); + + // solhint-disable-next-line func-name-mixedcase + function MAXIMUM_DELAY() external view returns (uint256); + + function admin() external view returns (address); + + function pendingAdmin() external view returns (address); + + function delay() external view returns (uint256); + + function queuedTransactions(bytes32) external view returns (bool); + + function setDelay(uint256) external; + + function acceptAdmin() external; + + function setPendingAdmin(address) external; + + function queueTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external returns (bytes32); + + function cancelTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external; + + function executeTransaction( + address target, + uint256 value, + string memory signature, + bytes memory data, + uint256 eta + ) external payable returns (bytes memory); +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/compound/LICENSE b/lib/openzeppelin-contracts/contracts/vendor/compound/LICENSE new file mode 100644 index 0000000..7da2324 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/compound/LICENSE @@ -0,0 +1,11 @@ +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. \ No newline at end of file diff --git a/lib/openzeppelin-contracts/contracts/vendor/optimism/ICrossDomainMessenger.sol b/lib/openzeppelin-contracts/contracts/vendor/optimism/ICrossDomainMessenger.sol new file mode 100644 index 0000000..cc01a48 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/optimism/ICrossDomainMessenger.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (vendor/optimism/ICrossDomainMessenger.sol) +pragma solidity >0.5.0 <0.9.0; + +/** + * @title ICrossDomainMessenger + */ +interface ICrossDomainMessenger { + /********** + * Events * + **********/ + + event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); + event RelayedMessage(bytes32 indexed msgHash); + event FailedRelayedMessage(bytes32 indexed msgHash); + + /************* + * Variables * + *************/ + + function xDomainMessageSender() external view returns (address); + + /******************** + * Public Functions * + ********************/ + + /** + * Sends a cross domain message to the target messenger. + * @param _target Target contract address. + * @param _message Message to send to the target. + * @param _gasLimit Gas limit for the provided message. + */ + function sendMessage(address _target, bytes calldata _message, uint32 _gasLimit) external; +} diff --git a/lib/openzeppelin-contracts/contracts/vendor/optimism/LICENSE b/lib/openzeppelin-contracts/contracts/vendor/optimism/LICENSE new file mode 100644 index 0000000..6a7da52 --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/optimism/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright 2020-2021 Optimism + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/openzeppelin-contracts/contracts/vendor/polygon/IFxMessageProcessor.sol b/lib/openzeppelin-contracts/contracts/vendor/polygon/IFxMessageProcessor.sol new file mode 100644 index 0000000..be73e6f --- /dev/null +++ b/lib/openzeppelin-contracts/contracts/vendor/polygon/IFxMessageProcessor.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (vendor/polygon/IFxMessageProcessor.sol) +pragma solidity ^0.8.0; + +interface IFxMessageProcessor { + function processMessageFromRoot(uint256 stateId, address rootMessageSender, bytes calldata data) external; +} diff --git a/lib/openzeppelin-contracts/docs/antora.yml b/lib/openzeppelin-contracts/docs/antora.yml new file mode 100644 index 0000000..513a997 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/antora.yml @@ -0,0 +1,6 @@ +name: contracts +title: Contracts +version: 4.x +nav: + - modules/ROOT/nav.adoc + - modules/api/nav.adoc diff --git a/lib/openzeppelin-contracts/docs/config.js b/lib/openzeppelin-contracts/docs/config.js new file mode 100644 index 0000000..f0af663 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/config.js @@ -0,0 +1,21 @@ +const path = require('path'); +const fs = require('fs'); + +/** @type import('solidity-docgen/dist/config').UserConfig */ +module.exports = { + outputDir: 'docs/modules/api/pages', + templates: 'docs/templates', + exclude: ['mocks'], + pageExtension: '.adoc', + pages: (_, file, config) => { + // For each contract file, find the closest README.adoc and return its location as the output page path. + const sourcesDir = path.resolve(config.root, config.sourcesDir); + let dir = path.resolve(config.root, file.absolutePath); + while (dir.startsWith(sourcesDir)) { + dir = path.dirname(dir); + if (fs.existsSync(path.join(dir, 'README.adoc'))) { + return path.relative(sourcesDir, dir) + config.pageExtension; + } + } + }, +}; diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-admin.png b/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-admin.png new file mode 100644 index 0000000..8265259 Binary files /dev/null and b/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-admin.png differ diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-vote.png b/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-vote.png new file mode 100644 index 0000000..76e9b3a Binary files /dev/null and b/lib/openzeppelin-contracts/docs/modules/ROOT/images/tally-vote.png differ diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/nav.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/nav.adoc new file mode 100644 index 0000000..6604c2d --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/nav.adoc @@ -0,0 +1,21 @@ +* xref:index.adoc[Overview] +* xref:wizard.adoc[Wizard] +* xref:extending-contracts.adoc[Extending Contracts] +* xref:upgradeable.adoc[Using with Upgrades] + +* xref:releases-stability.adoc[Releases & Stability] + +* xref:access-control.adoc[Access Control] + +* xref:tokens.adoc[Tokens] +** xref:erc20.adoc[ERC20] +*** xref:erc20-supply.adoc[Creating Supply] +** xref:erc721.adoc[ERC721] +** xref:erc777.adoc[ERC777] +** xref:erc1155.adoc[ERC1155] + +* xref:governance.adoc[Governance] + +* xref:crosschain.adoc[Crosschain] + +* xref:utilities.adoc[Utilities] diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/access-control.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/access-control.adoc new file mode 100644 index 0000000..f3ddb62 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/access-control.adoc @@ -0,0 +1,217 @@ += Access Control + +Access control—that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. It is therefore *critical* to understand how you implement it, lest someone else https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7[steals your whole system]. + +[[ownership-and-ownable]] +== Ownership and `Ownable` + +The most common and basic form of access control is the concept of _ownership_: there's an account that is the `owner` of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. + +OpenZeppelin Contracts provides xref:api:access.adoc#Ownable[`Ownable`] for implementing ownership in your contracts. + +[source,solidity] +---- +// contracts/MyContract.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract MyContract is Ownable { + function normalThing() public { + // anyone can call this normalThing() + } + + function specialThing() public onlyOwner { + // only the owner can call specialThing()! + } +} +---- + +By default, the xref:api:access.adoc#Ownable-owner--[`owner`] of an `Ownable` contract is the account that deployed it, which is usually exactly what you want. + +Ownable also lets you: + +* xref:api:access.adoc#Ownable-transferOwnership-address-[`transferOwnership`] from the owner account to a new one, and +* xref:api:access.adoc#Ownable-renounceOwnership--[`renounceOwnership`] for the owner to relinquish this administrative privilege, a common pattern after an initial stage with centralized administration is over. + +WARNING: Removing the owner altogether will mean that administrative tasks that are protected by `onlyOwner` will no longer be callable! + +Note that *a contract can also be the owner of another one*! This opens the door to using, for example, a https://gnosis-safe.io[Gnosis Safe], an https://aragon.org[Aragon DAO], or a totally custom contract that _you_ create. + +In this way, you can use _composability_ to add additional layers of access control complexity to your contracts. Instead of having a single regular Ethereum account (Externally Owned Account, or EOA) as the owner, you could use a 2-of-3 multisig run by your project leads, for example. Prominent projects in the space, such as https://makerdao.com[MakerDAO], use systems similar to this one. + +[[role-based-access-control]] +== Role-Based Access Control + +While the simplicity of _ownership_ can be useful for simple systems or quick prototyping, different levels of authorization are often needed. You may want for an account to have permission to ban users from a system, but not create new tokens. https://en.wikipedia.org/wiki/Role-based_access_control[_Role-Based Access Control (RBAC)_] offers flexibility in this regard. + +In essence, we will be defining multiple _roles_, each allowed to perform different sets of actions. An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for instead of simply using `onlyOwner`. This check can be enforced through the `onlyRole` modifier. Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. + +Most software uses access control systems that are role-based: some users are regular users, some may be supervisors or managers, and a few will often have administrative privileges. + +[[using-access-control]] +=== Using `AccessControl` + +OpenZeppelin Contracts provides xref:api:access.adoc#AccessControl[`AccessControl`] for implementing role-based access control. Its usage is straightforward: for each role that you want to define, +you will create a new _role identifier_ that is used to grant, revoke, and check if an account has that role. + +Here's a simple example of using `AccessControl` in an xref:tokens.adoc#ERC20[`ERC20` token] to define a 'minter' role, which allows accounts that have it create new tokens: + +[source,solidity] +---- +// contracts/MyToken.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyToken is ERC20, AccessControl { + // Create a new role identifier for the minter role + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + + constructor(address minter) ERC20("MyToken", "TKN") { + // Grant the minter role to a specified account + _grantRole(MINTER_ROLE, minter); + } + + function mint(address to, uint256 amount) public { + // Check that the calling account has the minter role + require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter"); + _mint(to, amount); + } +} +---- + +NOTE: Make sure you fully understand how xref:api:access.adoc#AccessControl[`AccessControl`] works before using it on your system, or copy-pasting the examples from this guide. + +While clear and explicit, this isn't anything we wouldn't have been able to achieve with `Ownable`. Indeed, where `AccessControl` shines is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. + +Let's augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens, and by using the `onlyRole` modifier: + +[source,solidity] +---- +// contracts/MyToken.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyToken is ERC20, AccessControl { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); + + constructor(address minter, address burner) ERC20("MyToken", "TKN") { + _grantRole(MINTER_ROLE, minter); + _grantRole(BURNER_ROLE, burner); + } + + function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public onlyRole(BURNER_ROLE) { + _burn(from, amount); + } +} +---- + +So clean! By splitting concerns this way, more granular levels of permission may be implemented than were possible with the simpler _ownership_ approach to access control. Limiting what each component of a system is able to do is known as the https://en.wikipedia.org/wiki/Principle_of_least_privilege[principle of least privilege], and is a good security practice. Note that each account may still have more than one role, if so desired. + +[[granting-and-revoking]] +=== Granting and Revoking Roles + +The ERC20 token example above uses `_grantRole`, an `internal` function that is useful when programmatically assigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? + +By default, **accounts with a role cannot grant it or revoke it from other accounts**: all having a role does is making the `hasRole` check pass. To grant and revoke roles dynamically, you will need help from the _role's admin_. + +Every role has an associated admin role, which grants permission to call the `grantRole` and `revokeRole` functions. A role can be granted or revoked by using these if the calling account has the corresponding admin role. Multiple roles may have the same admin role to make management easier. A role's admin can even be the same role itself, which would cause accounts with that role to be able to also grant and revoke it. + +This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also provides an easy way to manage simpler applications. `AccessControl` includes a special role, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. An account with this role will be able to manage any other role, unless `_setRoleAdmin` is used to select a new admin role. + +Let's take a look at the ERC20 token example, this time taking advantage of the default admin role: + +[source,solidity] +---- +// contracts/MyToken.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyToken is ERC20, AccessControl { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); + + constructor() ERC20("MyToken", "TKN") { + // Grant the contract deployer the default admin role: it will be able + // to grant and revoke any roles + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + } + + function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { + _mint(to, amount); + } + + function burn(address from, uint256 amount) public onlyRole(BURNER_ROLE) { + _burn(from, amount); + } +} +---- + +Note that, unlike the previous examples, no accounts are granted the 'minter' or 'burner' roles. However, because those roles' admin role is the default admin role, and _that_ role was granted to `msg.sender`, that same account can call `grantRole` to give minting or burning permission, and `revokeRole` to remove it. + +Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary over time. It can also be used to support use cases such as https://en.wikipedia.org/wiki/Know_your_customer[KYC], where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. + +[[querying-privileged-accounts]] +=== Querying Privileged Accounts + +Because accounts might <> dynamically, it is not always possible to determine which accounts hold a particular role. This is important as it allows to prove certain properties about a system, such as that an administrative account is a multisig or a DAO, or that a certain role has been removed from all users, effectively disabling any associated functionality. + +Under the hood, `AccessControl` uses `EnumerableSet`, a more powerful variant of Solidity's `mapping` type, which allows for key enumeration. `getRoleMemberCount` can be used to retrieve the number of accounts that have a particular role, and `getRoleMember` can then be called to get the address of each of these accounts. + +```javascript +const minterCount = await myToken.getRoleMemberCount(MINTER_ROLE); + +const members = []; +for (let i = 0; i < minterCount; ++i) { + members.push(await myToken.getRoleMember(MINTER_ROLE, i)); +} +``` + +== Delayed operation + +Access control is essential to prevent unauthorized access to critical functions. These functions may be used to mint tokens, freeze transfers or perform an upgrade that completely changes the smart contract logic. While xref:api:access.adoc#Ownable[`Ownable`] and xref:api:access.adoc#AccessControl[`AccessControl`] can prevent unauthorized access, they do not address the issue of a misbehaving administrator attacking their own system to the prejudice of their users. + +This is the issue the xref:api:governance.adoc#TimelockController[`TimelockController`] is addressing. + +The xref:api:governance.adoc#TimelockController[`TimelockController`] is a proxy that is governed by proposers and executors. When set as the owner/admin/controller of a smart contract, it ensures that whichever maintenance operation is ordered by the proposers is subject to a delay. This delay protects the users of the smart contract by giving them time to review the maintenance operation and exit the system if they consider it is in their best interest to do so. + +=== Using `TimelockController` + +By default, the address that deployed the xref:api:governance.adoc#TimelockController[`TimelockController`] gets administration privileges over the timelock. This role grants the right to assign proposers, executors, and other administrators. + +The first step in configuring the xref:api:governance.adoc#TimelockController[`TimelockController`] is to assign at least one proposer and one executor. These can be assigned during construction or later by anyone with the administrator role. These roles are not exclusive, meaning an account can have both roles. + +Roles are managed using the xref:api:access.adoc#AccessControl[`AccessControl`] interface and the `bytes32` values for each role are accessible through the `ADMIN_ROLE`, `PROPOSER_ROLE` and `EXECUTOR_ROLE` constants. + +There is an additional feature built on top of `AccessControl`: giving the executor role to `address(0)` opens access to anyone to execute a proposal once the timelock has expired. This feature, while useful, should be used with caution. + +At this point, with both a proposer and an executor assigned, the timelock can perform operations. + +An optional next step is for the deployer to renounce its administrative privileges and leave the timelock self-administered. If the deployer decides to do so, all further maintenance, including assigning new proposers/schedulers or changing the timelock duration will have to follow the timelock workflow. This links the governance of the timelock to the governance of contracts attached to the timelock, and enforce a delay on timelock maintenance operations. + +WARNING: If the deployer renounces administrative rights in favour of timelock itself, assigning new proposers or executors will require a timelocked operation. This means that if the accounts in charge of any of these two roles become unavailable, then the entire contract (and any contract it controls) becomes locked indefinitely. + +With both the proposer and executor roles assigned and the timelock in charge of its own administration, you can now transfer the ownership/control of any contract to the timelock. + +TIP: A recommended configuration is to grant both roles to a secure governance contract such as a DAO or a multisig, and to additionally grant the executor role to a few EOAs held by people in charge of helping with the maintenance operations. These wallets cannot take over control of the timelock but they can help smoothen the workflow. + +=== Minimum delay + +Operations executed by the xref:api:governance.adoc#TimelockController[`TimelockController`] are not subject to a fixed delay but rather a minimum delay. Some major updates might call for a longer delay. For example, if a delay of just a few days might be sufficient for users to audit a minting operation, it makes sense to use a delay of a few weeks, or even a few months, when scheduling a smart contract upgrade. + +The minimum delay (accessible through the xref:api:governance.adoc#TimelockController-getMinDelay--[`getMinDelay`] method) can be updated by calling the xref:api:governance.adoc#TimelockController-updateDelay-uint256-[`updateDelay`] function. Bear in mind that access to this function is only accessible by the timelock itself, meaning this maintenance operation has to go through the timelock itself. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crosschain.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crosschain.adoc new file mode 100644 index 0000000..cbe24df --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crosschain.adoc @@ -0,0 +1,210 @@ += Adding cross-chain support to contracts + +If your contract is targeting to be used in the context of multichain operations, you may need specific tools to identify and process these cross-chain operations. + +OpenZeppelin provides the xref:api:crosschain.adoc#CrossChainEnabled[`CrossChainEnabled`] abstract contract, that includes dedicated internal functions. + +In this guide, we will go through an example use case: _how to build an upgradeable & mintable ERC20 token controlled by a governor present on a foreign chain_. + +== Starting point, our ERC20 contract + +Let's start with a small ERC20 contract, that we bootstrapped using the https://wizard.openzeppelin.com/[OpenZeppelin Contracts Wizard], and extended with an owner that has the ability to mint. Note that for demonstration purposes we have not used the built-in `Ownable` contract. + +[source,solidity] +---- +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +contract MyToken is Initializable, ERC20Upgradeable, UUPSUpgradeable { + address public owner; + + modifier onlyOwner() { + require(owner == _msgSender(), "Not authorized"); + _; + } + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() initializer {} + + function initialize(address initialOwner) initializer public { + __ERC20_init("MyToken", "MTK"); + __UUPSUpgradeable_init(); + + owner = initialOwner; + } + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner { + } +} +---- + +This token is mintable and upgradeable by the owner of the contract. + +== Preparing our contract for cross-chain operations. + +Let's now imagine that this contract is going to live on one chain, but we want the minting and the upgrading to be performed by a xref:governance.adoc[`governor`] contract on another chain. + +For example, we could have our token on xDai, with our governor on mainnet, or we could have our token on mainnet, with our governor on optimism + +In order to do that, we will start by adding xref:api:crosschain.adoc#CrossChainEnabled[`CrossChainEnabled`] to our contract. You will notice that the contract is now abstract. This is because `CrossChainEnabled` is an abstract contract: it is not tied to any particular chain and it deals with cross-chain interactions in an abstract way. This is what enables us to easily reuse the code for different chains. We will specialize it later by inheriting from a chain-specific implementation of the abstraction. + +```diff + import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; ++import "@openzeppelin/contracts-upgradeable/crosschain/CrossChainEnabled.sol"; + +-contract MyToken is Initializable, ERC20Upgradeable, UUPSUpgradeable { ++abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, UUPSUpgradeable, CrossChainEnabled { +``` + +Once that is done, we can use the `onlyCrossChainSender` modifier, provided by `CrossChainEnabled` in order to protect the minting and upgrading operations. + +```diff +- function mint(address to, uint256 amount) public onlyOwner { ++ function mint(address to, uint256 amount) public onlyCrossChainSender(owner) { + +- function _authorizeUpgrade(address newImplementation) internal override onlyOwner { ++ function _authorizeUpgrade(address newImplementation) internal override onlyCrossChainSender(owner) { +``` + +This change will effectively restrict the mint and upgrade operations to the `owner` on the remote chain. + +== Specializing for a specific chain + +Once the abstract cross-chain version of our token is ready we can easily specialize it for the chain we want, or more precisely for the bridge system that we want to rely on. + +This is done using one of the many `CrossChainEnabled` implementations. + +For example, if our token is on xDai, and our governor on mainnet, we can use the https://docs.tokenbridge.net/amb-bridge/about-amb-bridge[AMB] bridge available on xDai at https://blockscout.com/xdai/mainnet/address/0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59[0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59] + +[source,solidity] +---- +[...] + +import "@openzeppelin/contracts-upgradeable/crosschain/amb/CrossChainEnabledAMB.sol"; + +contract MyTokenXDAI is + MyTokenCrossChain, + CrossChainEnabledAMB(0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59) +{} +---- + +If the token is on Ethereum mainnet, and our governor on Optimism, we use the Optimism https://community.optimism.io/docs/protocol/protocol-2.0/#l1crossdomainmessenger[CrossDomainMessenger] available on mainnet at https://etherscan.io/address/0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1[0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1]. + +[source,solidity] +---- +[...] + +import "@openzeppelin/contracts-upgradeable/crosschain/optimismCrossChainEnabledOptimism.sol"; + +contract MyTokenOptimism is + MyTokenCrossChain, + CrossChainEnabledOptimism(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1) +{} +---- + +== Mixing cross domain addresses is dangerous + +When designing a contract with cross-chain support, it is essential to understand possible fallbacks and the security assumption that are being made. + +In this guide, we are particularly focusing on restricting access to a specific caller. This is usually done (as shown above) using `msg.sender` or `_msgSender()`. However, when going cross-chain, it is not just that simple. Even without considering possible bridge issues, it is important to keep in mind that the same address can correspond to very different entities when considering a multi-chain space. EOA wallets can only execute operations if the wallet's private-key signs the transaction. To our knowledge this is the case in all EVM chains, so a cross-chain message coming from such a wallet is arguably equivalent to a non-cross-chain message by the same wallet. The situation is however very different for smart contracts. + +Due to the way smart contract addresses are computed, and the fact that smart contracts on different chains live independent lives, you could have two very different contracts live at the same address on different chains. You could imagine two multisig wallets with different signers using the same address on different chains. You could also see a very basic smart wallet live on one chain at the same address as a full-fledged governor on another chain. Therefore, you should be careful that whenever you give permissions to a specific address, you control with chain this address can act from. + +== Going further with access control + +In the previous example, we have both an `onlyOwner()` modifier and the `onlyCrossChainSender(owner)` mechanism. We didn't use the xref:access-control.adoc#ownership-and-ownable[`Ownable`] pattern because the ownership transfer mechanism in includes is not designed to work with the owner being a cross-chain entity. Unlike xref:access-control.adoc#ownership-and-ownable[`Ownable`], xref:access-control.adoc#role-based-access-control[`AccessControl`] is more effective at capturing the nuances and can effectively be used to build cross-chain-aware contracts. + +Using xref:api:access.adoc#AccessControlCrossChain[`AccessControlCrossChain`] includes both the xref:api:access.adoc#AccessControl[`AccessControl`] core and the xref:api:crosschain.adoc#CrossChainEnabled[`CrossChainEnabled`] abstraction. It also includes some binding to make role management compatible with cross-chain operations. + +In the case of the `mint` function, the caller must have the `MINTER_ROLE` when the call originates from the same chain. If the caller is on a remote chain, then the caller should not have the `MINTER_ROLE`, but the "aliased" version (`MINTER_ROLE ^ CROSSCHAIN_ALIAS`). This mitigates the danger described in the previous section by strictly separating local accounts from remote accounts from a different chain. See the xref:api:access.adoc#AccessControlCrossChain[`AccessControlCrossChain`] documentation for more details. + + +```diff + import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; ++import "@openzeppelin/contracts-upgradeable/access/AccessControlCrossChainUpgradeable.sol"; + +-abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, UUPSUpgradeable, CrossChainEnabled { ++abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, UUPSUpgradeable, AccessControlCrossChainUpgradeable { + +- address public owner; +- modifier onlyOwner() { +- require(owner == _msgSender(), "Not authorized"); +- _; +- } + ++ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); ++ bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); + + function initialize(address initialOwner) initializer public { + __ERC20_init("MyToken", "MTK"); + __UUPSUpgradeable_init(); ++ __AccessControl_init(); ++ _grantRole(_crossChainRoleAlias(DEFAULT_ADMIN_ROLE), initialOwner); // initialOwner is on a remote chain +- owner = initialOwner; + } + +- function mint(address to, uint256 amount) public onlyCrossChainSender(owner) { ++ function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { + +- function _authorizeUpgrade(address newImplementation) internal override onlyCrossChainSender(owner) { ++ function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) { +``` + +This results in the following, final, code: + +[source,solidity] +---- +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/AccessControlCrossChainUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +abstract contract MyTokenCrossChain is Initializable, ERC20Upgradeable, AccessControlCrossChainUpgradeable, UUPSUpgradeable { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() initializer {} + + function initialize(address initialOwner) initializer public { + __ERC20_init("MyToken", "MTK"); + __AccessControl_init(); + __UUPSUpgradeable_init(); + + _grantRole(_crossChainRoleAlias(DEFAULT_ADMIN_ROLE), initialOwner); // initialOwner is on a remote chain + } + + function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) { + _mint(to, amount); + } + + function _authorizeUpgrade(address newImplementation) internal onlyRole(UPGRADER_ROLE) override { + } +} + +import "@openzeppelin/contracts-upgradeable/crosschain/amb/CrossChainEnabledAMB.sol"; + +contract MyTokenXDAI is + MyTokenCrossChain, + CrossChainEnabledAMB(0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59) +{} + +import "@openzeppelin/contracts-upgradeable/crosschain/optimismCrossChainEnabledOptimism.sol"; + +contract MyTokenOptimism is + MyTokenCrossChain, + CrossChainEnabledOptimism(0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1) +{} +---- diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crowdsales.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crowdsales.adoc new file mode 100644 index 0000000..3757921 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/crowdsales.adoc @@ -0,0 +1,11 @@ += Crowdsales + +All crowdsale-related contracts were removed from the OpenZeppelin Contracts library on the https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256[v3.0.0 release] due to both a decline in their usage and the complexity associated with migrating them to Solidity v0.6. + +They are however still available on the v2.5 release of OpenZeppelin Contracts, which you can install by running: + +```console +$ npm install @openzeppelin/contracts@v2.5 +``` + +Refer to the https://docs.openzeppelin.com/contracts/2.x/crowdsales[v2.x documentation] when working with them. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/drafts.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/drafts.adoc new file mode 100644 index 0000000..b2c1ae6 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/drafts.adoc @@ -0,0 +1,19 @@ += Drafts + +All draft contracts were either moved into a different directory or removed from the OpenZeppelin Contracts library on the https://forum.openzeppelin.com/t/openzeppelin-contracts-v3-0-beta-release/2256[v3.0.0 release]. + +* `ERC20Migrator`: removed. +* xref:api:token/ERC20.adoc#ERC20Snapshot[`ERC20Snapshot`]: moved to `token/ERC20`. +* `ERC20Detailed` and `ERC1046`: removed. +* `TokenVesting`: removed. Pending a replacement that is being discussed in https://github.com/OpenZeppelin/openzeppelin-contracts/issues/1214[`#1214`]. +* xref:api:utils.adoc#Counters[`Counters`]: moved to xref:api:utils.adoc[`utils`]. +* xref:api:utils.adoc#Strings[`Strings`]: moved to xref:api:utils.adoc[`utils`]. +* xref:api:utils.adoc#SignedSafeMath[`SignedSafeMath`]: moved to xref:api:utils.adoc[`utils`]. + +Removed contracts are still available on the v2.5 release of OpenZeppelin Contracts, which you can install by running: + +```console +$ npm install @openzeppelin/contracts@v2.5 +``` + +Refer to the xref:2.x@contracts:api:drafts.adoc[v2.x documentation] when working with them. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc1155.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc1155.adoc new file mode 100644 index 0000000..0ca9338 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc1155.adoc @@ -0,0 +1,153 @@ += ERC1155 + +ERC1155 is a novel token standard that aims to take the best from previous standards to create a xref:tokens.adoc#different-kinds-of-tokens[*fungibility-agnostic*] and *gas-efficient* xref:tokens.adoc#but_first_coffee_a_primer_on_token_contracts[token contract]. + +TIP: ERC1155 draws ideas from all of xref:erc20.adoc[ERC20], xref:erc721.adoc[ERC721], and xref:erc777.adoc[ERC777]. If you're unfamiliar with those standards, head to their guides before moving on. + +[[multi-token-standard]] +== Multi Token Standard + +The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once. This is why its xref:api:token/ERC1155.adoc#IERC1155-balanceOf-address-uint256-[`balanceOf`] function differs from ERC20's and ERC777's: it has an additional `id` argument for the identifier of the token that you want to query the balance of. + +This is similar to how ERC721 does things, but in that standard a token `id` has no concept of balance: each token is non-fungible and exists or doesn't. The ERC721 xref:api:token/ERC721.adoc#IERC721-balanceOf-address-[`balanceOf`] function refers to _how many different tokens_ an account has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token `id`, and non-fungible tokens are implemented by simply minting a single one of them. + +This approach leads to massive gas savings for projects that require multiple tokens. Instead of deploying a new contract for each token type, a single ERC1155 token contract can hold the entire system state, reducing deployment costs and complexity. + +[[batch-operations]] +== Batch Operations + +Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, xref:api:token/ERC1155.adoc#IERC1155-balanceOfBatch-address---uint256---[`balanceOfBatch`] and xref:api:token/ERC1155.adoc#IERC1155-safeBatchTransferFrom-address-address-uint256---uint256---bytes-[`safeBatchTransferFrom`], that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. + +In the spirit of the standard, we've also included batch operations in the non-standard functions, such as xref:api:token/ERC1155.adoc#ERC1155-_mintBatch-address-uint256---uint256---bytes-[`_mintBatch`]. + +== Constructing an ERC1155 Token Contract + +We'll use ERC1155 to track multiple items in our game, which will each have their own unique attributes. We mint all items to the deployer of the contract, which we can later transfer to players. Players are free to keep their tokens or trade them with other people as they see fit, as they would any other asset on the blockchain! + +For simplicity, we will mint all items in the constructor, but you could add minting functionality to the contract to mint on demand to players. + +TIP: For an overview of minting mechanisms, check out xref:erc20-supply.adoc[Creating ERC20 Supply]. + +Here's what a contract for tokenized items might look like: + +[source,solidity] +---- +// contracts/GameItems.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +contract GameItems is ERC1155 { + uint256 public constant GOLD = 0; + uint256 public constant SILVER = 1; + uint256 public constant THORS_HAMMER = 2; + uint256 public constant SWORD = 3; + uint256 public constant SHIELD = 4; + + constructor() ERC1155("https://game.example/api/item/{id}.json") { + _mint(msg.sender, GOLD, 10**18, ""); + _mint(msg.sender, SILVER, 10**27, ""); + _mint(msg.sender, THORS_HAMMER, 1, ""); + _mint(msg.sender, SWORD, 10**9, ""); + _mint(msg.sender, SHIELD, 10**9, ""); + } +} +---- + +Note that for our Game Items, Gold is a fungible token whilst Thor's Hammer is a non-fungible token as we minted only one. + +The xref:api:token/ERC1155.adoc#ERC1155[`ERC1155`] contract includes the optional extension xref:api:token/ERC1155.adoc#IERC1155MetadataURI[`IERC1155MetadataURI`]. That's where the xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`uri`] function comes from: we use it to retrieve the metadata uri. + +Also note that, unlike ERC20, ERC1155 lacks a `decimals` field, since each token is distinct and cannot be partitioned. + +Once deployed, we will be able to query the deployer’s balance: +[source,javascript] +---- +> gameItems.balanceOf(deployerAddress,3) +1000000000 +---- + +We can transfer items to player accounts: +[source,javascript] +---- +> gameItems.safeTransferFrom(deployerAddress, playerAddress, 2, 1, "0x0") +> gameItems.balanceOf(playerAddress, 2) +1 +> gameItems.balanceOf(deployerAddress, 2) +0 +---- + +We can also batch transfer items to player accounts and get the balance of batches: +[source,javascript] +---- +> gameItems.safeBatchTransferFrom(deployerAddress, playerAddress, [0,1,3,4], [50,100,1,1], "0x0") +> gameItems.balanceOfBatch([playerAddress,playerAddress,playerAddress,playerAddress,playerAddress], [0,1,2,3,4]) +[50,100,1,1,1] +---- + +The metadata uri can be obtained: + +[source,javascript] +---- +> gameItems.uri(2) +"https://game.example/api/item/{id}.json" +---- + +The `uri` can include the string `++{id}++` which clients must replace with the actual token ID, in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters. + +For token ID `2` and uri `++https://game.example/api/item/{id}.json++` clients would replace `++{id}++` with `0000000000000000000000000000000000000000000000000000000000000002` to retrieve JSON at `https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json`. + +The JSON document for token ID 2 might look something like: + +[source,json] +---- +{ + "name": "Thor's hammer", + "description": "Mjölnir, the legendary hammer of the Norse god of thunder.", + "image": "https://game.example/item-id-8u5h2m.png", + "strength": 20 +} +---- + +For more information about the metadata JSON Schema, check out the https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema[ERC-1155 Metadata URI JSON Schema]. + +NOTE: You'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game! + +TIP: If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the URI information, but these techniques are out of the scope of this overview guide + +[[sending-to-contracts]] +== Sending Tokens to Contracts + +A key difference when using xref:api:token/ERC1155.adoc#IERC1155-safeTransferFrom-address-address-uint256-uint256-bytes-[`safeTransferFrom`] is that token transfers to other contracts may revert with the following message: + +[source,text] +---- +ERC1155: transfer to non ERC1155Receiver implementer +---- + +This is a good thing! It means that the recipient contract has not registered itself as aware of the ERC1155 protocol, so transfers to it are disabled to *prevent tokens from being locked forever*. As an example, https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d[the Golem contract currently holds over 350k `GNT` tokens], worth multiple tens of thousands of dollars, and lacks methods to get them out of there. This has happened to virtually every ERC20-backed project, usually due to user error. + +In order for our contract to receive ERC1155 tokens we can inherit from the convenience contract xref:api:token/ERC1155.adoc#ERC1155Holder[`ERC1155Holder`] which handles the registering for us. Though we need to remember to implement functionality to allow tokens to be transferred out of our contract: + +[source,solidity] +---- +// contracts/MyContract.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; + +contract MyContract is ERC1155Holder { +} +---- + +We can also implement more complex scenarios using the xref:api:token/ERC1155.adoc#IERC1155Receiver-onERC1155Received-address-address-uint256-uint256-bytes-[`onERC1155Received`] and xref:api:token/ERC1155.adoc#IERC1155Receiver-onERC1155BatchReceived-address-address-uint256---uint256---bytes-[`onERC1155BatchReceived`] functions. + +[[Presets]] +== Preset ERC1155 contract +A preset ERC1155 is available, https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol[`ERC1155PresetMinterPauser`]. It is preset to allow for token minting (create) - including batch minting, stop all token transfers (pause) and allow holders to burn (destroy) their tokens. The contract uses xref:access-control.adoc[Access Control] to control access to the minting and pausing functionality. The account that deploys the contract will be granted the minter and pauser roles, as well as the default admin role. + +This contract is ready to deploy without having to write any Solidity code. It can be used as-is for quick prototyping and testing, but is also suitable for production environments. + +NOTE: Contract presets are now deprecated in favor of https://wizard.openzeppelin.com[Contracts Wizard] as a more powerful alternative. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20-supply.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20-supply.adoc new file mode 100644 index 0000000..31b0cd9 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20-supply.adoc @@ -0,0 +1,113 @@ += Creating ERC20 Supply + +In this guide, you will learn how to create an ERC20 token with a custom supply mechanism. We will showcase two idiomatic ways to use OpenZeppelin Contracts for this purpose that you will be able to apply to your smart contract development practice. + +The standard interface implemented by tokens built on Ethereum is called ERC20, and Contracts includes a widely used implementation of it: the aptly named xref:api:token/ERC20.adoc[`ERC20`] contract. This contract, like the standard itself, is quite simple and bare-bones. In fact, if you try to deploy an instance of `ERC20` as-is it will be quite literally useless... it will have no supply! What use is a token with no supply? + +The way that supply is created is not defined in the ERC20 document. Every token is free to experiment with its own mechanisms, ranging from the most decentralized to the most centralized, from the most naive to the most researched, and more. + +[[fixed-supply]] +== Fixed Supply + +Let's say we want a token with a fixed supply of 1000, initially allocated to the account that deploys the contract. If you've used Contracts v1, you may have written code like the following: + +[source,solidity] +---- +contract ERC20FixedSupply is ERC20 { + constructor() { + totalSupply += 1000; + balances[msg.sender] += 1000; + } +} +---- + +Starting with Contracts v2, this pattern is not only discouraged, but disallowed. The variables `totalSupply` and `balances` are now private implementation details of `ERC20`, and you can't directly write to them. Instead, there is an internal xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`] function that will do exactly this: + +[source,solidity] +---- +contract ERC20FixedSupply is ERC20 { + constructor() ERC20("Fixed", "FIX") { + _mint(msg.sender, 1000); + } +} +---- + +Encapsulating state like this makes it safer to extend contracts. For instance, in the first example we had to manually keep the `totalSupply` in sync with the modified balances, which is easy to forget. In fact, we omitted something else that is also easily forgotten: the `Transfer` event that is required by the standard, and which is relied on by some clients. The second example does not have this bug, because the internal `_mint` function takes care of it. + +[[rewarding-miners]] +== Rewarding Miners + +The internal xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`] function is the key building block that allows us to write ERC20 extensions that implement a supply mechanism. + +The mechanism we will implement is a token reward for the miners that produce Ethereum blocks. In Solidity, we can access the address of the current block's miner in the global variable `block.coinbase`. We will mint a token reward to this address whenever someone calls the function `mintMinerReward()` on our token. The mechanism may sound silly, but you never know what kind of dynamic this might result in, and it's worth analyzing and experimenting with! + +[source,solidity] +---- +contract ERC20WithMinerReward is ERC20 { + constructor() ERC20("Reward", "RWD") {} + + function mintMinerReward() public { + _mint(block.coinbase, 1000); + } +} +---- + +As we can see, `_mint` makes it super easy to do this correctly. + +[[modularizing-the-mechanism]] +== Modularizing the Mechanism + +There is one supply mechanism already included in Contracts: `ERC20PresetMinterPauser`. This is a generic mechanism in which a set of accounts is assigned the `minter` role, granting them the permission to call a `mint` function, an external version of `_mint`. + +This can be used for centralized minting, where an externally owned account (i.e. someone with a pair of cryptographic keys) decides how much supply to create and for whom. There are very legitimate use cases for this mechanism, such as https://medium.com/reserve-currency/why-another-stablecoin-866f774afede#3aea[traditional asset-backed stablecoins]. + +The accounts with the minter role don't need to be externally owned, though, and can just as well be smart contracts that implement a trustless mechanism. We can in fact implement the same behavior as the previous section. + +[source,solidity] +---- +contract MinerRewardMinter { + ERC20PresetMinterPauser _token; + + constructor(ERC20PresetMinterPauser token) { + _token = token; + } + + function mintMinerReward() public { + _token.mint(block.coinbase, 1000); + } +} +---- + +This contract, when initialized with an `ERC20PresetMinterPauser` instance, and granted the `minter` role for that contract, will result in exactly the same behavior implemented in the previous section. What is interesting about using `ERC20PresetMinterPauser` is that we can easily combine multiple supply mechanisms by assigning the role to multiple contracts, and moreover that we can do this dynamically. + +TIP: To learn more about roles and permissioned systems, head to our xref:access-control.adoc[Access Control guide]. + +[[automating-the-reward]] +== Automating the Reward + +So far our supply mechanisms were triggered manually, but `ERC20` also allows us to extend the core functionality of the token through the xref:api:token/ERC20.adoc#ERC20-_beforeTokenTransfer-address-address-uint256-[`_beforeTokenTransfer`] hook (see xref:extending-contracts.adoc#using-hooks[Using Hooks]). + +Adding to the supply mechanism from previous sections, we can use this hook to mint a miner reward for every token transfer that is included in the blockchain. + +[source,solidity] +---- +contract ERC20WithAutoMinerReward is ERC20 { + constructor() ERC20("Reward", "RWD") {} + + function _mintMinerReward() internal { + _mint(block.coinbase, 1000); + } + + function _beforeTokenTransfer(address from, address to, uint256 value) internal virtual override { + if (!(from == address(0) && to == block.coinbase)) { + _mintMinerReward(); + } + super._beforeTokenTransfer(from, to, value); + } +} +---- + +[[wrapping-up]] +== Wrapping Up + +We've seen two ways to implement ERC20 supply mechanisms: internally through `_mint`, and externally through `ERC20PresetMinterPauser`. Hopefully this has helped you understand how to use OpenZeppelin Contracts and some of the design principles behind it, and you can apply them to your own smart contracts. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20.adoc new file mode 100644 index 0000000..bcb510c --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc20.adoc @@ -0,0 +1,85 @@ += ERC20 + +An ERC20 token contract keeps track of xref:tokens.adoc#different-kinds-of-tokens[_fungible_ tokens]: any one token is exactly equal to any other token; no tokens have special rights or behavior associated with them. This makes ERC20 tokens useful for things like a *medium of exchange currency*, *voting rights*, *staking*, and more. + +OpenZeppelin Contracts provides many ERC20-related contracts. On the xref:api:token/ERC20.adoc[`API reference`] you'll find detailed information on their properties and usage. + +[[constructing-an-erc20-token-contract]] +== Constructing an ERC20 Token Contract + +Using Contracts, we can easily create our own ERC20 token contract, which will be used to track _Gold_ (GLD), an internal currency in a hypothetical game. + +Here's what our GLD token might look like. + +[source,solidity] +---- +// contracts/GLDToken.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract GLDToken is ERC20 { + constructor(uint256 initialSupply) ERC20("Gold", "GLD") { + _mint(msg.sender, initialSupply); + } +} +---- + +Our contracts are often used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance], and here we're reusing xref:api:token/ERC20.adoc#erc20[`ERC20`] for both the basic standard implementation and the xref:api:token/ERC20.adoc#ERC20-name--[`name`], xref:api:token/ERC20.adoc#ERC20-symbol--[`symbol`], and xref:api:token/ERC20.adoc#ERC20-decimals--[`decimals`] optional extensions. Additionally, we're creating an `initialSupply` of tokens, which will be assigned to the address that deploys the contract. + +TIP: For a more complete discussion of ERC20 supply mechanisms, see xref:erc20-supply.adoc[Creating ERC20 Supply]. + +That's it! Once deployed, we will be able to query the deployer's balance: + +[source,javascript] +---- +> GLDToken.balanceOf(deployerAddress) +1000000000000000000000 +---- + +We can also xref:api:token/ERC20.adoc#IERC20-transfer-address-uint256-[transfer] these tokens to other accounts: + +[source,javascript] +---- +> GLDToken.transfer(otherAddress, 300000000000000000000) +> GLDToken.balanceOf(otherAddress) +300000000000000000000 +> GLDToken.balanceOf(deployerAddress) +700000000000000000000 +---- + +[[a-note-on-decimals]] +== A Note on `decimals` + +Often, you'll want to be able to divide your tokens into arbitrary amounts: say, if you own `5 GLD`, you may want to send `1.5 GLD` to a friend, and keep `3.5 GLD` to yourself. Unfortunately, Solidity and the EVM do not support this behavior: only integer (whole) numbers can be used, which poses an issue. You may send `1` or `2` tokens, but not `1.5`. + +To work around this, xref:api:token/ERC20.adoc#ERC20[`ERC20`] provides a xref:api:token/ERC20.adoc#ERC20-decimals--[`decimals`] field, which is used to specify how many decimal places a token has. To be able to transfer `1.5 GLD`, `decimals` must be at least `1`, since that number has a single decimal place. + +How can this be achieved? It's actually very simple: a token contract can use larger integer values, so that a balance of `50` will represent `5 GLD`, a transfer of `15` will correspond to `1.5 GLD` being sent, and so on. + +It is important to understand that `decimals` is _only used for display purposes_. All arithmetic inside the contract is still performed on integers, and it is the different user interfaces (wallets, exchanges, etc.) that must adjust the displayed values according to `decimals`. The total token supply and balance of each account are not specified in `GLD`: you need to divide by `10 ** decimals` to get the actual `GLD` amount. + +You'll probably want to use a `decimals` value of `18`, just like Ether and most ERC20 token contracts in use, unless you have a very special reason not to. When minting tokens or transferring them around, you will be actually sending the number `num GLD * (10 ** decimals)`. + +NOTE: By default, `ERC20` uses a value of `18` for `decimals`. To use a different value, you will need to override the `decimals()` function in your contract. + +```solidity +function decimals() public view virtual override returns (uint8) { + return 16; +} +``` + +So if you want to send `5` tokens using a token contract with 18 decimals, the method to call will actually be: + +```solidity +transfer(recipient, 5 * (10 ** 18)); +``` + +[[Presets]] +== Preset ERC20 contract +A preset ERC20 is available, https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol[`ERC20PresetMinterPauser`]. It is preset to allow for token minting (create), stop all token transfers (pause) and allow holders to burn (destroy) their tokens. The contract uses xref:access-control.adoc[Access Control] to control access to the minting and pausing functionality. The account that deploys the contract will be granted the minter and pauser roles, as well as the default admin role. + +This contract is ready to deploy without having to write any Solidity code. It can be used as-is for quick prototyping and testing, but is also suitable for production environments. + +NOTE: Contract presets are now deprecated in favor of https://wizard.openzeppelin.com[Contracts Wizard] as a more powerful alternative. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc721.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc721.adoc new file mode 100644 index 0000000..add40d5 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc721.adoc @@ -0,0 +1,90 @@ += ERC721 + +We've discussed how you can make a _fungible_ token using xref:erc20.adoc[ERC20], but what if not all tokens are alike? This comes up in situations like *real estate*, *voting rights*, or *collectibles*, where some items are valued more than others, due to their usefulness, rarity, etc. ERC721 is a standard for representing ownership of xref:tokens.adoc#different-kinds-of-tokens[_non-fungible_ tokens], that is, where each token is unique. + +ERC721 is a more complex standard than ERC20, with multiple optional extensions, and is split across a number of contracts. The OpenZeppelin Contracts provide flexibility regarding how these are combined, along with custom useful extensions. Check out the xref:api:token/ERC721.adoc[API Reference] to learn more about these. + +== Constructing an ERC721 Token Contract + +We'll use ERC721 to track items in our game, which will each have their own unique attributes. Whenever one is to be awarded to a player, it will be minted and sent to them. Players are free to keep their token or trade it with other people as they see fit, as they would any other asset on the blockchain! Please note any account can call `awardItem` to mint items. To restrict what accounts can mint items we can add xref:access-control.adoc[Access Control]. + +Here's what a contract for tokenized items might look like: + +[source,solidity] +---- +// contracts/GameItem.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; + +contract GameItem is ERC721URIStorage { + using Counters for Counters.Counter; + Counters.Counter private _tokenIds; + + constructor() ERC721("GameItem", "ITM") {} + + function awardItem(address player, string memory tokenURI) + public + returns (uint256) + { + uint256 newItemId = _tokenIds.current(); + _mint(player, newItemId); + _setTokenURI(newItemId, tokenURI); + + _tokenIds.increment(); + return newItemId; + } +} +---- + +The xref:api:token/ERC721.adoc#ERC721URIStorage[`ERC721URIStorage`] contract is an implementation of ERC721 that includes the metadata standard extensions (xref:api:token/ERC721.adoc#IERC721Metadata[`IERC721Metadata`]) as well as a mechanism for per-token metadata. That's where the xref:api:token/ERC721.adoc#ERC721-_setTokenURI-uint256-string-[`_setTokenURI`] method comes from: we use it to store an item's metadata. + +Also note that, unlike ERC20, ERC721 lacks a `decimals` field, since each token is distinct and cannot be partitioned. + +New items can be created: + +[source,javascript] +---- +> gameItem.awardItem(playerAddress, "https://game.example/item-id-8u5h2m.json") +Transaction successful. Transaction hash: 0x... +Events emitted: + - Transfer(0x0000000000000000000000000000000000000000, playerAddress, 7) +---- + +And the owner and metadata of each item queried: + +[source,javascript] +---- +> gameItem.ownerOf(7) +playerAddress +> gameItem.tokenURI(7) +"https://game.example/item-id-8u5h2m.json" +---- + +This `tokenURI` should resolve to a JSON document that might look something like: + +[source,json] +---- +{ + "name": "Thor's hammer", + "description": "Mjölnir, the legendary hammer of the Norse god of thunder.", + "image": "https://game.example/item-id-8u5h2m.png", + "strength": 20 +} +---- + +For more information about the `tokenURI` metadata JSON Schema, check out the https://eips.ethereum.org/EIPS/eip-721[ERC721 specification]. + +NOTE: You'll notice that the item's information is included in the metadata, but that information isn't on-chain! So a game developer could change the underlying metadata, changing the rules of the game! + +TIP: If you'd like to put all item information on-chain, you can extend ERC721 to do so (though it will be rather costly) by providing a xref:utilities.adoc#base64[`Base64`] Data URI with the JSON schema encoded. You could also leverage IPFS to store the tokenURI information, but these techniques are out of the scope of this overview guide. + +[[Presets]] +== Preset ERC721 contract +A preset ERC721 is available, https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.7/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol[`ERC721PresetMinterPauserAutoId`]. It is preconfigured with token minting (creation) with token ID and URI auto generation, the ability to stop all token transfers (pause), and it allows holders to burn (destroy) their tokens. The contract uses xref:access-control.adoc[Access Control] to control access to the minting and pausing functionality. The account that deploys the contract will be granted the minter and pauser roles, as well as the default admin role. + +This contract is ready to deploy without having to write any Solidity code. It can be used as-is for quick prototyping and testing but is also suitable for production environments. + +NOTE: Contract presets are now deprecated in favor of https://wizard.openzeppelin.com[Contracts Wizard] as a more powerful alternative. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc777.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc777.adoc new file mode 100644 index 0000000..d79fbee --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/erc777.adoc @@ -0,0 +1,73 @@ += ERC777 + +Like xref:erc20.adoc[ERC20], ERC777 is a standard for xref:tokens.adoc#different-kinds-of-tokens[_fungible_ tokens], and is focused around allowing more complex interactions when trading tokens. More generally, it brings tokens and Ether closer together by providing the equivalent of a `msg.value` field, but for tokens. + +The standard also brings multiple quality-of-life improvements, such as getting rid of the confusion around `decimals`, minting and burning with proper events, among others, but its killer feature is *receive hooks*. A hook is simply a function in a contract that is called when tokens are sent to it, meaning *accounts and contracts can react to receiving tokens*. + +This enables a lot of interesting use cases, including atomic purchases using tokens (no need to do `approve` and `transferFrom` in two separate transactions), rejecting reception of tokens (by reverting on the hook call), redirecting the received tokens to other addresses (similarly to how xref:api:payment#PaymentSplitter[`PaymentSplitter`] does it), among many others. + +Furthermore, since contracts are required to implement these hooks in order to receive tokens, _no tokens can get stuck in a contract that is unaware of the ERC777 protocol_, as has happened countless times when using ERC20s. + +== What If I Already Use ERC20? + +The standard has you covered! The ERC777 standard is *backwards compatible with ERC20*, meaning you can interact with these tokens as if they were ERC20, using the standard functions, while still getting all of the niceties, including send hooks. See the https://eips.ethereum.org/EIPS/eip-777#backward-compatibility[EIP's Backwards Compatibility section] to learn more. + +== Constructing an ERC777 Token Contract + +We will replicate the `GLD` example of the xref:erc20.adoc#constructing-an-erc20-token-contract[ERC20 guide], this time using ERC777. As always, check out the xref:api:token/ERC777.adoc[`API reference`] to learn more about the details of each function. + +[source,solidity] +---- +// contracts/GLDToken.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; + +contract GLDToken is ERC777 { + constructor(uint256 initialSupply, address[] memory defaultOperators) + ERC777("Gold", "GLD", defaultOperators) + { + _mint(msg.sender, initialSupply, "", ""); + } +} +---- + +In this case, we'll be extending from the xref:api:token/ERC777.adoc#ERC777[`ERC777`] contract, which provides an implementation with compatibility support for ERC20. The API is quite similar to that of xref:api:token/ERC777.adoc#ERC777[`ERC777`], and we'll once again make use of xref:api:token/ERC777.adoc#ERC777-_mint-address-address-uint256-bytes-bytes-[`_mint`] to assign the `initialSupply` to the deployer account. Unlike xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[ERC20's `_mint`], this one includes some extra parameters, but you can safely ignore those for now. + +You'll notice both xref:api:token/ERC777.adoc#IERC777-name--[`name`] and xref:api:token/ERC777.adoc#IERC777-symbol--[`symbol`] are assigned, but not xref:api:token/ERC777.adoc#ERC777-decimals--[`decimals`]. The ERC777 specification makes it mandatory to include support for these functions (unlike ERC20, where it is optional and we had to include xref:api:token/ERC20.adoc#ERC20Detailed[`ERC20Detailed`]), but also mandates that `decimals` always returns a fixed value of `18`, so there's no need to set it ourselves. For a review of ``decimals``'s role and importance, refer back to our xref:erc20.adoc#a-note-on-decimals[ERC20 guide]. + +Finally, we'll need to set the xref:api:token/ERC777.adoc#IERC777-defaultOperators--[`defaultOperators`]: special accounts (usually other smart contracts) that will be able to transfer tokens on behalf of their holders. If you're not planning on using operators in your token, you can simply pass an empty array. _Stay tuned for an upcoming in-depth guide on ERC777 operators!_ + +That's it for a basic token contract! We can now deploy it, and use the same xref:api:token/ERC777.adoc#IERC777-balanceOf-address-[`balanceOf`] method to query the deployer's balance: + +[source,javascript] +---- +> GLDToken.balanceOf(deployerAddress) +1000 +---- + +To move tokens from one account to another, we can use both xref:api:token/ERC777.adoc#ERC777-transfer-address-uint256-[``ERC20``'s `transfer`] method, or the new xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[``ERC777``'s `send`], which fulfills a very similar role, but adds an optional `data` field: + +[source,javascript] +---- +> GLDToken.transfer(otherAddress, 300) +> GLDToken.send(otherAddress, 300, "") +> GLDToken.balanceOf(otherAddress) +600 +> GLDToken.balanceOf(deployerAddress) +400 +---- + +== Sending Tokens to Contracts + +A key difference when using xref:api:token/ERC777.adoc#ERC777-send-address-uint256-bytes-[`send`] is that token transfers to other contracts may revert with the following message: + +[source,text] +---- +ERC777: token recipient contract has no implementer for ERC777TokensRecipient +---- + +This is a good thing! It means that the recipient contract has not registered itself as aware of the ERC777 protocol, so transfers to it are disabled to *prevent tokens from being locked forever*. As an example, https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d[the Golem contract currently holds over 350k `GNT` tokens], worth multiple tens of thousands of dollars, and lacks methods to get them out of there. This has happened to virtually every ERC20-backed project, usually due to user error. + +_An upcoming guide will cover how a contract can register itself as a recipient, send and receive hooks, and other advanced features of ERC777!_ diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/extending-contracts.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/extending-contracts.adoc new file mode 100644 index 0000000..6ae68a8 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/extending-contracts.adoc @@ -0,0 +1,129 @@ += Extending Contracts + +Most of the OpenZeppelin Contracts are expected to be used via https://solidity.readthedocs.io/en/latest/contracts.html#inheritance[inheritance]: you will _inherit_ from them when writing your own contracts. + +This is the commonly found `is` syntax, like in `contract MyToken is ERC20`. + +[NOTE] +==== +Unlike ``contract``s, Solidity ``library``s are not inherited from and instead rely on the https://solidity.readthedocs.io/en/latest/contracts.html#using-for[`using for`] syntax. + +OpenZeppelin Contracts has some ``library``s: most are in the xref:api:utils.adoc[Utils] directory. +==== + +== Overriding + +Inheritance is often used to add the parent contract's functionality to your own contract, but that's not all it can do. You can also _change_ how some parts of the parent behave using _overrides_. + +For example, imagine you want to change xref:api:access.adoc#AccessControl[`AccessControl`] so that xref:api:access.adoc#AccessControl-revokeRole-bytes32-address-[`revokeRole`] can no longer be called. This can be achieved using overrides: + +```solidity +// contracts/ModifiedAccessControl.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; + +contract ModifiedAccessControl is AccessControl { + // Override the revokeRole function + function revokeRole(bytes32, address) public override { + revert("ModifiedAccessControl: cannot revoke roles"); + } +} +``` + +The old `revokeRole` is then replaced by our override, and any calls to it will immediately revert. We cannot _remove_ the function from the contract, but reverting on all calls is good enough. + +=== Calling `super` + +Sometimes you want to _extend_ a parent's behavior, instead of outright changing it to something else. This is where `super` comes in. + +The `super` keyword will let you call functions defined in a parent contract, even if they are overridden. This mechanism can be used to add additional checks to a function, emit events, or otherwise add functionality as you see fit. + +TIP: For more information on how overrides work, head over to the https://solidity.readthedocs.io/en/latest/contracts.html#index-17[official Solidity documentation]. + +Here is a modified version of xref:api:access.adoc#AccessControl[`AccessControl`] where xref:api:access.adoc#AccessControl-revokeRole-bytes32-address-[`revokeRole`] cannot be used to revoke the `DEFAULT_ADMIN_ROLE`: + + +```solidity +// contracts/ModifiedAccessControl.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/access/AccessControl.sol"; + +contract ModifiedAccessControl is AccessControl { + function revokeRole(bytes32 role, address account) public override { + require( + role != DEFAULT_ADMIN_ROLE, + "ModifiedAccessControl: cannot revoke default admin role" + ); + + super.revokeRole(role, account); + } +} +``` + +The `super.revokeRole` statement at the end will invoke ``AccessControl``'s original version of `revokeRole`, the same code that would've run if there were no overrides in place. + +NOTE: As of v3.0.0, `view` functions are not `virtual` in OpenZeppelin, and therefore cannot be overridden. We're considering https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2154[lifting this restriction] in an upcoming release. Let us know if this is something you care about! + +[[using-hooks]] +== Using Hooks + +Sometimes, in order to extend a parent contract you will need to override multiple related functions, which leads to code duplication and increased likelihood of bugs. + +For example, consider implementing safe xref:api:token/ERC20.adoc#ERC20[`ERC20`] transfers in the style of xref:api:token/ERC721.adoc#IERC721Receiver[`IERC721Receiver`]. You may think overriding xref:api:token/ERC20.adoc#ERC20-transfer-address-uint256-[`transfer`] and xref:api:token/ERC20.adoc#ERC20-transferFrom-address-address-uint256-[`transferFrom`] would be enough, but what about xref:api:token/ERC20.adoc#ERC20-_transfer-address-address-uint256-[`_transfer`] and xref:api:token/ERC20.adoc#ERC20-_mint-address-uint256-[`_mint`]? To prevent you from having to deal with these details, we introduced **hooks**. + +Hooks are simply functions that are called before or after some action takes place. They provide a centralized point to _hook into_ and extend the original behavior. + +Here's how you would implement the `IERC721Receiver` pattern in `ERC20`, using the xref:api:token/ERC20.adoc#ERC20-_beforeTokenTransfer-address-address-uint256-[`_beforeTokenTransfer`] hook: + +```solidity +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract ERC20WithSafeTransfer is ERC20 { + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal virtual override + { + super._beforeTokenTransfer(from, to, amount); + + require(_validRecipient(to), "ERC20WithSafeTransfer: invalid recipient"); + } + + function _validRecipient(address to) private view returns (bool) { + ... + } + + ... +} +``` + +Using hooks this way leads to cleaner and safer code, without having to rely on a deep understanding of the parent's internals. + +=== Rules of Hooks + +There's a few guidelines you should follow when writing code that uses hooks in order to prevent issues. They are very simple, but do make sure you follow them: + +1. Whenever you override a parent's hook, re-apply the `virtual` attribute to the hook. That will allow child contracts to add more functionality to the hook. +2. **Always** call the parent's hook in your override using `super`. This will make sure all hooks in the inheritance tree are called: contracts like xref:api:token/ERC20.adoc#ERC20Pausable[`ERC20Pausable`] rely on this behavior. + +```solidity +contract MyToken is ERC20 { + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal virtual override // Add virtual here! + { + super._beforeTokenTransfer(from, to, amount); // Call parent hook + ... + } +} +``` +That's it! Enjoy simpler code using hooks! + +== Security + +The maintainers of OpenZeppelin Contracts are mainly concerned with the correctness and security of the code as published in the library, and the combinations of base contracts with the official extensions from the library. + +Custom overrides, and those of hooks in particular, may break some important assumptions and introduce vulnerabilities in otherwise secure code. While we try to ensure the contracts remain secure in the face of a wide range of potential customizations, this is done in a best-effort manner. Similarly, while we try to document all important assumptions, this should not be relied upon. Custom overrides should be carefully reviewed and checked against the source code of the contract they are customizing so as to fully understand their impact and guarantee their security. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/governance.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/governance.adoc new file mode 100644 index 0000000..00a997d --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/governance.adoc @@ -0,0 +1,339 @@ += How to set up on-chain governance + +In this guide we will learn how OpenZeppelin’s Governor contract works, how to set it up, and how to use it to create proposals, vote for them, and execute them, using tools provided by Ethers.js and Tally. + +NOTE: Find detailed contract documentation at xref:api:governance.adoc[Governance API]. + +== Introduction + +Decentralized protocols are in constant evolution from the moment they are publicly released. Often, the initial team retains control of this evolution in the first stages, but eventually delegates it to a community of stakeholders. The process by which this community makes decisions is called on-chain governance, and it has become a central component of decentralized protocols, fueling varied decisions such as parameter tweaking, smart contract upgrades, integrations with other protocols, treasury management, grants, etc. + +This governance protocol is generally implemented in a special-purpose contract called “Governor”. The GovernorAlpha and GovernorBravo contracts designed by Compound have been very successful and popular so far, with the downside that projects with different requirements have had to fork the code to customize it for their needs, which can pose a high risk of introducing security issues. For OpenZeppelin Contracts, we set out to build a modular system of Governor contracts so that forking is not needed, and different requirements can be accommodated by writing small modules using Solidity inheritance. You will find the most common requirements out of the box in OpenZeppelin Contracts, but writing additional ones is simple, and we will be adding new features as requested by the community in future releases. Additionally, the design of OpenZeppelin Governor requires minimal use of storage and results in more gas efficient operation. + +== Compatibility + +OpenZeppelin’s Governor system was designed with a concern for compatibility with existing systems that were based on Compound’s GovernorAlpha and GovernorBravo. Because of this, you will find that many modules are presented in two variants, one of which is built for compatibility with those systems. + +=== ERC20Votes & ERC20VotesComp + +The ERC20 extension to keep track of votes and vote delegation is one such case. The shorter one is the more generic version because it can support token supplies greater than 2^96, while the “Comp” variant is limited in that regard, but exactly fits the interface of the COMP token that is used by GovernorAlpha and Bravo. Both contract variants share the same events, so they are fully compatible when looking at events only. + +=== Governor & GovernorCompatibilityBravo + +An OpenZeppelin Governor contract is by default not interface-compatible with GovernorAlpha or Bravo, since some of the functions are different or missing, although it shares all of the same events. However, it’s possible to opt in to full compatibility by inheriting from the GovernorCompatibilityBravo module. The contract will be cheaper to deploy and use without this module. + +=== GovernorTimelockControl & GovernorTimelockCompound + +When using a timelock with your Governor contract, you can use either OpenZeppelin’s TimelockController or Compound’s Timelock. Based on the choice of timelock, you should choose the corresponding Governor module: GovernorTimelockControl or GovernorTimelockCompound respectively. This allows you to migrate an existing GovernorAlpha instance to an OpenZeppelin-based Governor without changing the timelock in use. + +=== Tally + +https://www.tally.xyz[Tally] is a full-fledged application for user owned on-chain governance. It comprises a voting dashboard, proposal creation wizard, real time research and analysis, and educational content. + +For all of these options, the Governor will be compatible with Tally: users will be able to create proposals, visualize voting power and advocates, navigate proposals, and cast votes. For proposal creation in particular, projects can also use Defender Admin as an alternative interface. + +In the rest of this guide, we will focus on a fresh deploy of the vanilla OpenZeppelin Governor features without concern for compatibility with GovernorAlpha or Bravo. + +== Setup + +=== Token + +The voting power of each account in our governance setup will be determined by an ERC20 token. The token has to implement the ERC20Votes extension. This extension will keep track of historical balances so that voting power is retrieved from past snapshots rather than current balance, which is an important protection that prevents double voting. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; + +contract MyToken is ERC20, ERC20Permit, ERC20Votes { + constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {} + + // The functions below are overrides required by Solidity. + + function _afterTokenTransfer(address from, address to, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._afterTokenTransfer(from, to, amount); + } + + function _mint(address to, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._mint(to, amount); + } + + function _burn(address account, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._burn(account, amount); + } +} +``` + +If your project already has a live token that does not include ERC20Votes and is not upgradeable, you can wrap it in a governance token by using ERC20Wrapper. This will allow token holders to participate in governance by wrapping their tokens 1-to-1. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol"; + +contract MyToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper { + constructor(IERC20 wrappedToken) + ERC20("MyToken", "MTK") + ERC20Permit("MyToken") + ERC20Wrapper(wrappedToken) + {} + + // The functions below are overrides required by Solidity. + + function _afterTokenTransfer(address from, address to, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._afterTokenTransfer(from, to, amount); + } + + function _mint(address to, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._mint(to, amount); + } + + function _burn(address account, uint256 amount) + internal + override(ERC20, ERC20Votes) + { + super._burn(account, amount); + } +} +``` + +NOTE: Voting power could be determined in different ways: multiple ERC20 tokens, ERC721 tokens, sybil resistant identities, etc. All of these options are potentially supported by writing a custom Votes module for your Governor. The only other source of voting power available in OpenZeppelin Contracts currently is xref:api:token/ERC721.adoc#ERC721Votes[`ERC721Votes`]. + +=== Governor + +Initially, we will build a Governor without a timelock. The core logic is given by the Governor contract, but we still need to choose: 1) how voting power is determined, 2) how many votes are needed for quorum, 3) what options people have when casting a vote and how those votes are counted, and 4) what type of token should be used to vote. Each of these aspects is customizable by writing your own module, or more easily choosing one from OpenZeppelin Contracts. + +For 1) we will use the GovernorVotes module, which hooks to an IVotes instance to determine the voting power of an account based on the token balance they hold when a proposal becomes active. This module requires as a constructor parameter the address of the token. + +For 2) we will use GovernorVotesQuorumFraction which works together with ERC20Votes to define quorum as a percentage of the total supply at the block a proposal’s voting power is retrieved. This requires a constructor parameter to set the percentage. Most Governors nowadays use 4%, so we will initialize the module with parameter 4 (this indicates the percentage, resulting in 4%). + +For 3) we will use GovernorCountingSimple, a module that offers 3 options to voters: For, Against, and Abstain, and where only For and Abstain votes are counted towards quorum. + +Besides these modules, Governor itself has some parameters we must set. + +votingDelay: How long after a proposal is created should voting power be fixed. A large voting delay gives users time to unstake tokens if necessary. + +votingPeriod: How long does a proposal remain open to votes. + +These parameters are specified in number of blocks. Assuming block time of around 13.14 seconds, we will set votingDelay = 1 day = 6570 blocks, and votingPeriod = 1 week = 45992 blocks. + +We can optionally set a proposal threshold as well. This restricts proposal creation to accounts who have enough voting power. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +import "./governance/Governor.sol"; +import "./governance/compatibility/GovernorCompatibilityBravo.sol"; +import "./governance/extensions/GovernorVotes.sol"; +import "./governance/extensions/GovernorVotesQuorumFraction.sol"; +import "./governance/extensions/GovernorTimelockControl.sol"; + +contract MyGovernor is Governor, GovernorCompatibilityBravo, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl { + constructor(IVotes _token, TimelockController _timelock) + Governor("MyGovernor") + GovernorVotes(_token) + GovernorVotesQuorumFraction(4) + GovernorTimelockControl(_timelock) + {} + + function votingDelay() public pure override returns (uint256) { + return 6575; // 1 day + } + + function votingPeriod() public pure override returns (uint256) { + return 46027; // 1 week + } + + function proposalThreshold() public pure override returns (uint256) { + return 0; + } + + // The functions below are overrides required by Solidity. + + function quorum(uint256 blockNumber) + public + view + override(IGovernor, GovernorVotesQuorumFraction) + returns (uint256) + { + return super.quorum(blockNumber); + } + + function getVotes(address account, uint256 blockNumber) + public + view + override(IGovernor, GovernorVotes) + returns (uint256) + { + return super.getVotes(account, blockNumber); + } + + function state(uint256 proposalId) + public + view + override(Governor, IGovernor, GovernorTimelockControl) + returns (ProposalState) + { + return super.state(proposalId); + } + + function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description) + public + override(Governor, GovernorCompatibilityBravo, IGovernor) + returns (uint256) + { + return super.propose(targets, values, calldatas, description); + } + + function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash) + internal + override(Governor, GovernorTimelockControl) + returns (uint256) + { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() + internal + view + override(Governor, GovernorTimelockControl) + returns (address) + { + return super._executor(); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(Governor, IERC165, GovernorTimelockControl) + returns (bool) + { + return super.supportsInterface(interfaceId); + } +} + +``` + +=== Timelock + +It is good practice to add a timelock to governance decisions. This allows users to exit the system if they disagree with a decision before it is executed. We will use OpenZeppelin’s TimelockController in combination with the GovernorTimelockControl module. + +IMPORTANT: When using a timelock, it is the timelock that will execute proposals and thus the timelock that should hold any funds, ownership, and access control roles. Before version 4.5 there was no way to recover funds in the Governor contract when using a timelock! Before version 4.3, when using the Compound Timelock, ETH in the timelock was not easily accessible. + +TimelockController uses an AccessControl setup that we need to understand in order to set up roles. + +- The Proposer role is in charge of queueing operations: this is the role the Governor instance should be granted, and it should likely be the only proposer in the system. +- The Executor role is in charge of executing already available operations: we can assign this role to the special zero address to allow anyone to execute (if operations can be particularly time sensitive, the Governor should be made Executor instead). +- Lastly, there is the Admin role, which can grant and revoke the two previous roles: this is a very sensitive role that will be granted automatically to the timelock itself, and optionally to a second account, which can be used for ease of setup but should promptly renounce the role. + +== Proposal Lifecycle + +Let’s walk through how to create and execute a proposal on our newly deployed Governor. + +A proposal is a sequence of actions that the Governor contract will perform if it passes. Each action consists of a target address, calldata encoding a function call, and an amount of ETH to include. Additionally, a proposal includes a human-readable description. + +=== Create a Proposal + +Let’s say we want to create a proposal to give a team a grant, in the form of ERC20 tokens from the governance treasury. This proposal will consist of a single action where the target is the ERC20 token, calldata is the encoded function call `transfer(, )`, and with 0 ETH attached. + +Generally a proposal will be created with the help of an interface such as Tally or Defender. Here we will show how to create the proposal using Ethers.js. + +First we get all the parameters necessary for the proposal action. + +```javascript +const tokenAddress = ...; +const token = await ethers.getContractAt(‘ERC20’, tokenAddress); + +const teamAddress = ...; +const grantAmount = ...; +const transferCalldata = token.interface.encodeFunctionData(‘transfer’, [teamAddress, grantAmount]); +``` + +Now we are ready to call the propose function of the governor. Note that we don’t pass in one array of actions, but instead three arrays corresponding to the list of targets, the list of values, and the list of calldatas. In this case it’s a single action, so it’s simple: + +```javascript +await governor.propose( + [tokenAddress], + [0], + [transferCalldata], + “Proposal #1: Give grant to team”, +); +``` + +This will create a new proposal, with a proposal id that is obtained by hashing together the proposal data, and which will also be found in an event in the logs of the transaction. + +=== Cast a Vote + +Once a proposal is active, delegates can cast their vote. Note that it is delegates who carry voting power: if a token holder wants to participate, they can set a trusted representative as their delegate, or they can become a delegate themselves by self-delegating their voting power. + +Votes are cast by interacting with the Governor contract through the `castVote` family of functions. Voters would generally invoke this from a governance UI such as Tally. + +image::tally-vote.png[Voting in Tally] + +=== Execute the Proposal + +Once the voting period is over, if quorum was reached (enough voting power participated) and the majority voted in favor, the proposal is considered successful and can proceed to be executed. This can also be done in Tally in the "Administration Panel" section of a project. + +image::tally-admin.png[Administration Panel in Tally] + +We will see now how to do this manually using Ethers.js. + +If a timelock was set up, the first step to execution is queueing. You will notice that both the queue and execute functions require passing in the entire proposal parameters, as opposed to just the proposal id. This is necessary because this data is not stored on chain, as a measure to save gas. Note that these parameters can always be found in the events emitted by the contract. The only parameter that is not sent in its entirety is the description, since this is only needed in its hashed form to compute the proposal id. + +To queue, we call the queue function: + +```javascript +const descriptionHash = ethers.utils.id(“Proposal #1: Give grant to team”); + +await governor.queue( + [tokenAddress], + [0], + [transferCalldata], + descriptionHash, +); +``` + +This will cause the governor to interact with the timelock contract and queue the actions for execution after the required delay. + +After enough time has passed (according to the timelock parameters), the proposal can be executed. If there was no timelock to begin with, this step can be ran immediately after the proposal succeeds. + +```javascript +await governor.execute( + [tokenAddress], + [0], + [transferCalldata], + descriptionHash, +); +``` + +Executing the proposal will transfer the ERC20 tokens to the chosen recipient. To wrap up: we set up a system where a treasury is controlled by the collective decision of the token holders of a project, and all actions are executed via proposals enforced by on-chain votes. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/index.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/index.adoc new file mode 100644 index 0000000..5b64f05 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/index.adoc @@ -0,0 +1,63 @@ += Contracts + +*A library for secure smart contract development.* Build on a solid foundation of community-vetted code. + + * Implementations of standards like xref:erc20.adoc[ERC20] and xref:erc721.adoc[ERC721]. + * Flexible xref:access-control.adoc[role-based permissioning] scheme. + * Reusable xref:utilities.adoc[Solidity components] to build custom contracts and complex decentralized systems. + +== Overview + +[[install]] +=== Installation + +```console +$ npm install @openzeppelin/contracts +``` + +OpenZeppelin Contracts features a xref:releases-stability.adoc#api-stability[stable API], which means your contracts won't break unexpectedly when upgrading to a newer minor version. + +[[usage]] +=== Usage + +Once installed, you can use the contracts in the library by importing them: + +[source,solidity] +---- +// contracts/MyNFT.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract MyNFT is ERC721 { + constructor() ERC721("MyNFT", "MNFT") { + } +} +---- + +TIP: If you're new to smart contract development, head to xref:learn::developing-smart-contracts.adoc[Developing Smart Contracts] to learn about creating a new project and compiling your contracts. + +To keep your system secure, you should **always** use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself. The library is designed so that only the contracts and functions you use are deployed, so you don't need to worry about it needlessly increasing gas costs. + +[[security]] +== Security + +Please report any security issues you find via our https://www.immunefi.com/bounty/openzeppelin[bug bounty program on Immunefi] or directly to security@openzeppelin.org. + +[[next-steps]] +== Learn More + +The guides in the sidebar will teach about different concepts, and how to use the related contracts that OpenZeppelin Contracts provides: + +* xref:access-control.adoc[Access Control]: decide who can perform each of the actions on your system. +* xref:tokens.adoc[Tokens]: create tradable assets or collectibles, like the well known xref:erc20.adoc[ERC20] and xref:erc721.adoc[ERC721] standards. +* xref:utilities.adoc[Utilities]: generic useful tools, including non-overflowing math, signature verification, and trustless paying systems. + +The xref:api:token/ERC20.adoc[full API] is also thoroughly documented, and serves as a great reference when developing your smart contract application. You can also ask for help or follow Contracts' development in the https://forum.openzeppelin.com[community forum]. + +Finally, you may want to take a look at the https://blog.openzeppelin.com/guides/[guides on our blog], which cover several common use cases and good practices. The following articles provide great background reading, though please note, some of the referenced tools have changed as the tooling in the ecosystem continues to rapidly evolve. + +* https://blog.openzeppelin.com/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05[The Hitchhiker’s Guide to Smart Contracts in Ethereum] will help you get an overview of the various tools available for smart contract development, and help you set up your environment. +* https://blog.openzeppelin.com/a-gentle-introduction-to-ethereum-programming-part-1-783cc7796094[A Gentle Introduction to Ethereum Programming, Part 1] provides very useful information on an introductory level, including many basic concepts from the Ethereum platform. +* For a more in-depth dive, you may read the guide https://blog.openzeppelin.com/designing-the-architecture-for-your-ethereum-application-9cec086f8317[Designing the architecture for your Ethereum application], which discusses how to better structure your application and its relationship to the real world. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/releases-stability.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/releases-stability.adoc new file mode 100644 index 0000000..9a33103 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/releases-stability.adoc @@ -0,0 +1,85 @@ += New Releases and API Stability + +Developing smart contracts is hard, and a conservative approach towards dependencies is sometimes favored. However, it is also very important to stay on top of new releases: these may include bug fixes, or deprecate old patterns in favor of newer and better practices. + +Here we describe when you should expect new releases to come out, and how this affects you as a user of OpenZeppelin Contracts. + +[[release-schedule]] +== Release Schedule + +OpenZeppelin Contracts follows a <>. + +We aim for a new minor release every 1 or 2 months. + +[[minor-releases]] +=== Release Candidates + +Before every release, we publish a feature-frozen release candidate. The purpose of the release candidate is to have a period where the community can review the new code before the actual release. If important problems are discovered, several more release candidates may be required. After a week of no more changes to the release candidate, the new version is published. + +[[major-releases]] +=== Major Releases + +After several months or a year, a new major release may come out. These are not scheduled, but will be based on the need to release breaking changes such as a redesign of a core feature of the library (e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/pulls/2112[access control] in 3.0). Since we value stability, we aim for these to happen infrequently (expect no less than six months between majors). However, we may be forced to release one when there are big changes to the Solidity language. + +[[api-stability]] +== API Stability + +On the https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v2.0.0[OpenZeppelin Contracts 2.0 release], we committed ourselves to keeping a stable API. We aim to more precisely define what we understand by _stable_ and _API_ here, so users of the library can understand these guarantees and be confident their project won't break unexpectedly. + +In a nutshell, the API being stable means _if your project is working today, it will continue to do so after a minor upgrade_. New contracts and features will be added in minor releases, but only in a backwards compatible way. + +[[versioning-scheme]] +=== Versioning Scheme + +We follow https://semver.org/[SemVer], which means API breakage may occur between major releases (which <>). + +[[solidity-functions]] +=== Solidity Functions + +While the internal implementation of functions may change, their semantics and signature will remain the same. The domain of their arguments will not be less restrictive (e.g. if transferring a value of 0 is disallowed, it will remain disallowed), nor will general state restrictions be lifted (e.g. `whenPaused` modifiers). + +If new functions are added to a contract, it will be in a backwards-compatible way: their usage won't be mandatory, and they won't extend functionality in ways that may foreseeably break an application (e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/issues/1512[an `internal` method may be added to make it easier to retrieve information that was already available]). + +[[internal]] +==== `internal` + +This extends not only to `external` and `public` functions, but also `internal` ones: many contracts are meant to be used by inheriting them (e.g. `Pausable`, `PullPayment`, `AccessControl`), and are therefore used by calling these functions. Similarly, since all OpenZeppelin Contracts state variables are `private`, they can only be accessed this way (e.g. to create new `ERC20` tokens, instead of manually modifying `totalSupply` and `balances`, `_mint` should be called). + +`private` functions have no guarantees on their behavior, usage, or existence. + +Finally, sometimes language limitations will force us to e.g. make `internal` a function that should be `private` in order to implement features the way we want to. These cases will be well documented, and the normal stability guarantees won't apply. + +[[libraries]] +=== Libraries + +Some of our Solidity libraries use ``struct``s to handle internal data that should not be accessed directly (e.g. `Counter`). There's an https://github.com/ethereum/solidity/issues/4637[open issue] in the Solidity repository requesting a language feature to prevent said access, but it looks like it won't be implemented any time soon. Because of this, we will use leading underscores and mark said `struct` s to make it clear to the user that its contents and layout are _not_ part of the API. + +[[events]] +=== Events + +No events will be removed, and their arguments won't be changed in any way. New events may be added in later versions, and existing events may be emitted under new, reasonable circumstances (e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/issues/707[from 2.1 on, `ERC20` also emits `Approval` on `transferFrom` calls]). + +[[drafts]] +=== Drafts + +Some contracts implement EIPs that are still in Draft status, recognizable by a file name beginning with `draft-`, such as `utils/cryptography/draft-EIP712.sol`. Due to their nature as drafts, the details of these contracts may change and we cannot guarantee their stability. Minor releases of OpenZeppelin Contracts may contain breaking changes for the contracts labelled as Drafts, which will be duly announced in the https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/CHANGELOG.md[changelog]. The EIPs included are used by projects in production and this may make them less likely to change significantly. + +[[gas-costs]] +=== Gas Costs + +While attempts will generally be made to lower the gas costs of working with OpenZeppelin Contracts, there are no guarantees regarding this. In particular, users should not assume gas costs will not increase when upgrading library versions. + +[[bugfixes]] +=== Bug Fixes + +The API stability guarantees may need to be broken in order to fix a bug, and we will do so. This decision won't be made lightly however, and all options will be explored to make the change as non-disruptive as possible. When sufficient, contracts or functions which may result in unsafe behavior will be deprecated instead of removed (e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1543[#1543] and https://github.com/OpenZeppelin/openzeppelin-contracts/pull/1550[#1550]). + +[[solidity-compiler-version]] +=== Solidity Compiler Version + +Starting on version 0.5.0, the Solidity team switched to a faster release cycle, with minor releases every few weeks (v0.5.0 was released on November 2018, and v0.5.5 on March 2019), and major, breaking-change releases every couple of months (with v0.6.0 released on December 2019 and v0.7.0 on July 2020). Including the compiler version in OpenZeppelin Contract's stability guarantees would therefore force the library to either stick to old compilers, or release frequent major updates simply to keep up with newer Solidity releases. + +Because of this, *the minimum required Solidity compiler version is not part of the stability guarantees*, and users may be required to upgrade their compiler when using newer versions of Contracts. Bug fixes will still be backported to past major releases so that all versions currently in use receive these updates. + +You can read more about the rationale behind this, the other options we considered and why we went down this path https://github.com/OpenZeppelin/openzeppelin-contracts/issues/1498#issuecomment-449191611[here]. + diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/tokens.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/tokens.adoc new file mode 100644 index 0000000..b168756 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/tokens.adoc @@ -0,0 +1,32 @@ += Tokens + +Ah, the "token": blockchain's most powerful and most misunderstood tool. + +A token is a _representation of something in the blockchain_. This something can be money, time, services, shares in a company, a virtual pet, anything. By representing things as tokens, we can allow smart contracts to interact with them, exchange them, create or destroy them. + +[[but_first_coffee_a_primer_on_token_contracts]] +== But First, [strikethrough]#Coffee# a Primer on Token Contracts + +Much of the confusion surrounding tokens comes from two concepts getting mixed up: _token contracts_ and the actual _tokens_. + +A _token contract_ is simply an Ethereum smart contract. "Sending tokens" actually means "calling a method on a smart contract that someone wrote and deployed". At the end of the day, a token contract is not much more than a mapping of addresses to balances, plus some methods to add and subtract from those balances. + +It is these balances that represent the _tokens_ themselves. Someone "has tokens" when their balance in the token contract is non-zero. That's it! These balances could be considered money, experience points in a game, deeds of ownership, or voting rights, and each of these tokens would be stored in different token contracts. + +[[different-kinds-of-tokens]] +== Different Kinds of Tokens + +Note that there's a big difference between having two voting rights and two deeds of ownership: each vote is equal to all others, but houses usually are not! This is called https://en.wikipedia.org/wiki/Fungibility[fungibility]. _Fungible goods_ are equivalent and interchangeable, like Ether, fiat currencies, and voting rights. _Non-fungible_ goods are unique and distinct, like deeds of ownership, or collectibles. + +In a nutshell, when dealing with non-fungibles (like your house) you care about _which ones_ you have, while in fungible assets (like your bank account statement) what matters is _how much_ you have. + +== Standards + +Even though the concept of a token is simple, they have a variety of complexities in the implementation. Because everything in Ethereum is just a smart contract, and there are no rules about what smart contracts have to do, the community has developed a variety of *standards* (called EIPs or ERCs) for documenting how a contract can interoperate with other contracts. + +You've probably heard of the ERC20 or ERC721 token standards, and that's why you're here. Head to our specialized guides to learn more about these: + + * xref:erc20.adoc[ERC20]: the most widespread token standard for fungible assets, albeit somewhat limited by its simplicity. + * xref:erc721.adoc[ERC721]: the de-facto solution for non-fungible tokens, often used for collectibles and games. + * xref:erc777.adoc[ERC777]: a richer standard for fungible tokens, enabling new use cases and building on past learnings. Backwards compatible with ERC20. + * xref:erc1155.adoc[ERC1155]: a novel standard for multi-tokens, allowing for a single contract to represent multiple fungible and non-fungible tokens, along with batched operations for increased gas efficiency. diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/upgradeable.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/upgradeable.adoc new file mode 100644 index 0000000..2b8d272 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/upgradeable.adoc @@ -0,0 +1,73 @@ += Using with Upgrades + +If your contract is going to be deployed with upgradeability, such as using the xref:upgrades-plugins::index.adoc[OpenZeppelin Upgrades Plugins], you will need to use the Upgradeable variant of OpenZeppelin Contracts. + +This variant is available as a separate package called `@openzeppelin/contracts-upgradeable`, which is hosted in the repository https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable[OpenZeppelin/openzeppelin-contracts-upgradeable]. + +It follows all of the rules for xref:upgrades-plugins::writing-upgradeable.adoc[Writing Upgradeable Contracts]: constructors are replaced by initializer functions, state variables are initialized in initializer functions, and we additionally check for storage incompatibilities across minor versions. + +TIP: OpenZeppelin provides a full suite of tools for deploying and securing upgradeable smart contracts. xref:openzeppelin::upgrades.adoc[Check out the full list of resources]. + +== Overview + +=== Installation + +```console +$ npm install @openzeppelin/contracts-upgradeable +``` + +=== Usage + +The package replicates the structure of the main OpenZeppelin Contracts package, but every file and contract has the suffix `Upgradeable`. + +```diff +-import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; ++import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; + +-contract MyCollectible is ERC721 { ++contract MyCollectible is ERC721Upgradeable { +``` + +Constructors are replaced by internal initializer functions following the naming convention `+__{ContractName}_init+`. Since these are internal, you must always define your own public initializer function and call the parent initializer of the contract you extend. + +```diff +- constructor() ERC721("MyCollectible", "MCO") public { ++ function initialize() initializer public { ++ __ERC721_init("MyCollectible", "MCO"); + } +``` + +CAUTION: Use with multiple inheritance requires special attention. See the section below titled <>. + +Once this contract is set up and compiled, you can deploy it using the xref:upgrades-plugins::index.adoc[Upgrades Plugins]. The following snippet shows an example deployment script using Hardhat. + +```js +// scripts/deploy-my-collectible.js +const { ethers, upgrades } = require("hardhat"); + +async function main() { + const MyCollectible = await ethers.getContractFactory("MyCollectible"); + + const mc = await upgrades.deployProxy(MyCollectible); + + await mc.deployed(); + console.log("MyCollectible deployed to:", mc.address); +} + +main(); +``` + +== Further Notes + +[[multiple-inheritance]] +=== Multiple Inheritance + +Initializer functions are not linearized by the compiler like constructors. Because of this, each `+__{ContractName}_init+` function embeds the linearized calls to all parent initializers. As a consequence, calling two of these `init` functions can potentially initialize the same contract twice. + +The function `+__{ContractName}_init_unchained+` found in every contract is the initializer function minus the calls to parent initializers, and can be used to avoid the double initialization problem, but doing this manually is not recommended. We hope to be able to implement safety checks for this in future versions of the Upgrades Plugins. + +=== Storage Gaps + +You may notice that every contract includes a state variable named `+__gap+`. This is empty reserved space in storage that is put in place in Upgradeable contracts. It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments. + +It isn't safe to simply add a state variable because it "shifts down" all of the state variables below in the inheritance chain. This makes the storage layouts incompatible, as explained in xref:upgrades-plugins::writing-upgradeable.adoc#modifying-your-contracts[Writing Upgradeable Contracts]. The size of the `+__gap+` array is calculated so that the amount of storage used by a contract always adds up to the same number (in this case 50 storage slots). diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/utilities.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/utilities.adoc new file mode 100644 index 0000000..4231a6a --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/utilities.adoc @@ -0,0 +1,190 @@ += Utilities + +The OpenZeppelin Contracts provide a ton of useful utilities that you can use in your project. Here are some of the more popular ones. + +[[cryptography]] +== Cryptography + +=== Checking Signatures On-Chain + +xref:api:utils.adoc#ECDSA[`ECDSA`] provides functions for recovering and managing Ethereum account ECDSA signatures. These are often generated via https://web3js.readthedocs.io/en/v1.7.3/web3-eth.html#sign[`web3.eth.sign`], and are a 65 byte array (of type `bytes` in Solidity) arranged the following way: `[[v (1)], [r (32)], [s (32)]]`. + +The data signer can be recovered with xref:api:utils.adoc#ECDSA-recover-bytes32-bytes-[`ECDSA.recover`], and its address compared to verify the signature. Most wallets will hash the data to sign and add the prefix '\x19Ethereum Signed Message:\n', so when attempting to recover the signer of an Ethereum signed message hash, you'll want to use xref:api:utils.adoc#ECDSA-toEthSignedMessageHash-bytes32-[`toEthSignedMessageHash`]. + +[source,solidity] +---- +using ECDSA for bytes32; + +function _verify(bytes32 data, bytes memory signature, address account) internal pure returns (bool) { + return data + .toEthSignedMessageHash() + .recover(signature) == account; +} +---- + +WARNING: Getting signature verification right is not trivial: make sure you fully read and understand xref:api:utils.adoc#ECDSA[`ECDSA`]'s documentation. + +=== Verifying Merkle Proofs + +xref:api:utils.adoc#MerkleProof[`MerkleProof`] provides: + +* xref:api:utils.adoc#MerkleProof-verify-bytes32---bytes32-bytes32-[`verify`] - can prove that some value is part of a https://en.wikipedia.org/wiki/Merkle_tree[Merkle tree]. + +* xref:api:utils.adoc#MerkleProof-multiProofVerify-bytes32-bytes32---bytes32---bool---[`multiProofVerify`] - can prove multiple values are part of a Merkle tree. + +[[introspection]] +== Introspection + +In Solidity, it's frequently helpful to know whether or not a contract supports an interface you'd like to use. ERC165 is a standard that helps do runtime interface detection. Contracts provide helpers both for implementing ERC165 in your contracts and querying other contracts: + +* xref:api:utils.adoc#IERC165[`IERC165`] — this is the ERC165 interface that defines xref:api:utils.adoc#IERC165-supportsInterface-bytes4-[`supportsInterface`]. When implementing ERC165, you'll conform to this interface. +* xref:api:utils.adoc#ERC165[`ERC165`] — inherit this contract if you'd like to support interface detection using a lookup table in contract storage. You can register interfaces using xref:api:utils.adoc#ERC165-_registerInterface-bytes4-[`_registerInterface(bytes4)`]: check out example usage as part of the ERC721 implementation. +* xref:api:utils.adoc#ERC165Checker[`ERC165Checker`] — ERC165Checker simplifies the process of checking whether or not a contract supports an interface you care about. +* include with `using ERC165Checker for address;` +* xref:api:utils.adoc#ERC165Checker-_supportsInterface-address-bytes4-[`myAddress._supportsInterface(bytes4)`] +* xref:api:utils.adoc#ERC165Checker-_supportsAllInterfaces-address-bytes4---[`myAddress._supportsAllInterfaces(bytes4[\])`] + +[source,solidity] +---- +contract MyContract { + using ERC165Checker for address; + + bytes4 private InterfaceId_ERC721 = 0x80ac58cd; + + /** + * @dev transfer an ERC721 token from this contract to someone else + */ + function transferERC721( + address token, + address to, + uint256 tokenId + ) + public + { + require(token.supportsInterface(InterfaceId_ERC721), "IS_NOT_721_TOKEN"); + IERC721(token).transferFrom(address(this), to, tokenId); + } +} +---- + +[[math]] +== Math + +The most popular math related library OpenZeppelin Contracts provides is xref:api:utils.adoc#SafeMath[`SafeMath`], which provides mathematical functions that protect your contract from overflows and underflows. + +Include the contract with `using SafeMath for uint256;` and then call the functions: + +* `myNumber.add(otherNumber)` +* `myNumber.sub(otherNumber)` +* `myNumber.div(otherNumber)` +* `myNumber.mul(otherNumber)` +* `myNumber.mod(otherNumber)` + +Easy! + +[[payment]] +== Payment + +Want to split some payments between multiple people? Maybe you have an app that sends 30% of art purchases to the original creator and 70% of the profits to the current owner; you can build that with xref:api:finance.adoc#PaymentSplitter[`PaymentSplitter`]! + +In Solidity, there are some security concerns with blindly sending money to accounts, since it allows them to execute arbitrary code. You can read up on these security concerns in the https://consensys.github.io/smart-contract-best-practices/[Ethereum Smart Contract Best Practices] website. One of the ways to fix reentrancy and stalling problems is, instead of immediately sending Ether to accounts that need it, you can use xref:api:security.adoc#PullPayment[`PullPayment`], which offers an xref:api:security.adoc#PullPayment-_asyncTransfer-address-uint256-[`_asyncTransfer`] function for sending money to something and requesting that they xref:api:security.adoc#PullPayment-withdrawPayments-address-payable-[`withdrawPayments()`] it later. + +If you want to Escrow some funds, check out xref:api:utils.adoc#Escrow[`Escrow`] and xref:api:utils.adoc#ConditionalEscrow[`ConditionalEscrow`] for governing the release of some escrowed Ether. + +[[collections]] +== Collections + +If you need support for more powerful collections than Solidity's native arrays and mappings, take a look at xref:api:utils.adoc#EnumerableSet[`EnumerableSet`] and xref:api:utils.adoc#EnumerableMap[`EnumerableMap`]. They are similar to mappings in that they store and remove elements in constant time and don't allow for repeated entries, but they also support _enumeration_, which means you can easily query all stored entries both on and off-chain. + +[[misc]] +== Misc + +Want to check if an address is a contract? Use xref:api:utils.adoc#Address[`Address`] and xref:api:utils.adoc#Address-isContract-address-[`Address.isContract()`]. + +Want to keep track of some numbers that increment by 1 every time you want another one? Check out xref:api:utils.adoc#Counters[`Counters`]. This is useful for lots of things, like creating incremental identifiers, as shown on the xref:erc721.adoc[ERC721 guide]. + +=== Base64 + +xref:api:utils.adoc#Base64[`Base64`] util allows you to transform `bytes32` data into its Base64 `string` representation. + +This is especially useful for building URL-safe tokenURIs for both xref:api:token/ERC721.adoc#IERC721Metadata-tokenURI-uint256-[`ERC721`] or xref:api:token/ERC1155.adoc#IERC1155MetadataURI-uri-uint256-[`ERC1155`]. This library provides a clever way to serve URL-safe https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs/[Data URI] compliant strings to serve on-chain data structures. + +Here is an example to send JSON Metadata through a Base64 Data URI using an ERC721: + +[source, solidity] +---- +// contracts/My721Token.sol +// SPDX-License-Identifier: MIT + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/Base64.sol"; + +contract My721Token is ERC721 { + using Strings for uint256; + + constructor() ERC721("My721Token", "MTK") {} + + ... + + function tokenURI(uint256 tokenId) + public + pure + override + returns (string memory) + { + bytes memory dataURI = abi.encodePacked( + '{', + '"name": "My721Token #', tokenId.toString(), '"', + // Replace with extra ERC721 Metadata properties + '}' + ); + + return string( + abi.encodePacked( + "data:application/json;base64,", + Base64.encode(dataURI) + ) + ); + } +} +---- + +=== Multicall + +The `Multicall` abstract contract comes with a `multicall` function that bundles together multiple calls in a single external call. With it, external accounts may perform atomic operations comprising several function calls. This is not only useful for EOAs to make multiple calls in a single transaction, it's also a way to revert a previous call if a later one fails. + +Consider this dummy contract: + +[source,solidity] +---- +// contracts/Box.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/Multicall.sol"; + +contract Box is Multicall { + function foo() public { + ... + } + + function bar() public { + ... + } +} +---- + +This is how to call the `multicall` function using Truffle, allowing `foo` and `bar` to be called in a single transaction: +[source,javascript] +---- +// scripts/foobar.js + +const Box = artifacts.require('Box'); +const instance = await Box.new(); + +await instance.multicall([ + instance.contract.methods.foo().encodeABI(), + instance.contract.methods.bar().encodeABI() +]); +---- diff --git a/lib/openzeppelin-contracts/docs/modules/ROOT/pages/wizard.adoc b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/wizard.adoc new file mode 100644 index 0000000..2625053 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/modules/ROOT/pages/wizard.adoc @@ -0,0 +1,15 @@ += Contracts Wizard +:page-notoc: + +Not sure where to start? Use the interactive generator below to bootstrap your +contract and learn about the components offered in OpenZeppelin Contracts. + +TIP: Place the resulting contract in your `contracts` directory in order to compile it with a tool like Hardhat or Truffle. Consider reading our guide on xref:learn::developing-smart-contracts.adoc[Developing Smart Contracts] for more guidance! + +++++ + + + +++++ + + diff --git a/lib/openzeppelin-contracts/docs/templates/contract.hbs b/lib/openzeppelin-contracts/docs/templates/contract.hbs new file mode 100644 index 0000000..d97e7fd --- /dev/null +++ b/lib/openzeppelin-contracts/docs/templates/contract.hbs @@ -0,0 +1,85 @@ +{{#each items}} +:{{name}}: pass:normal[xref:#{{anchor}}[`++{{name}}++`]] +{{/each}} + +[.contract] +[[{{anchor}}]] +=== `++{{name}}++` link:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v{{oz-version}}/{{__item_context.file.absolutePath}}[{github-icon},role=heading-link] + +[.hljs-theme-light.nopadding] +```solidity +import "@openzeppelin/{{__item_context.file.absolutePath}}"; +``` + +{{{natspec.dev}}} + +{{#if modifiers}} +[.contract-index] +.Modifiers +-- +{{#each modifiers}} +* {xref-{{anchor~}} }[`++{{name}}({{names params}})++`] +{{/each}} +-- +{{/if}} + +{{#if has-functions}} +[.contract-index] +.Functions +-- +{{#each inherited-functions}} +{{#unless @first}} +[.contract-subindex-inherited] +.{{contract.name}} +{{/unless}} +{{#each functions}} +* {xref-{{anchor~}} }[`++{{name}}({{names params}})++`] +{{/each}} + +{{/each}} +-- +{{/if}} + +{{#if has-events}} +[.contract-index] +.Events +-- +{{#each inheritance}} +{{#unless @first}} +[.contract-subindex-inherited] +.{{name}} +{{/unless}} +{{#each events}} +* {xref-{{anchor~}} }[`++{{name}}({{names params}})++`] +{{/each}} + +{{/each}} +-- +{{/if}} + +{{#each modifiers}} +[.contract-item] +[[{{anchor}}]] +==== `[.contract-item-name]#++{{name}}++#++({{typed-params params}})++` [.item-kind]#modifier# + +{{{natspec.dev}}} + +{{/each}} + +{{#each functions}} +[.contract-item] +[[{{anchor}}]] +==== `[.contract-item-name]#++{{name}}++#++({{typed-params params}}){{#if returns}} → {{typed-params returns}}{{/if}}++` [.item-kind]#{{visibility}}# + +{{{natspec.dev}}} + +{{/each}} + +{{#each events}} +[.contract-item] +[[{{anchor}}]] +==== `[.contract-item-name]#++{{name}}++#++({{typed-params params}})++` [.item-kind]#event# + +{{{natspec.dev}}} + +{{/each}} diff --git a/lib/openzeppelin-contracts/docs/templates/helpers.js b/lib/openzeppelin-contracts/docs/templates/helpers.js new file mode 100644 index 0000000..8c94d75 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/templates/helpers.js @@ -0,0 +1,46 @@ +const { version } = require('../../package.json'); + +module.exports['oz-version'] = () => version; + +module.exports['readme-path'] = (opts) => { + return 'contracts/' + opts.data.root.id.replace(/\.adoc$/, '') + '/README.adoc'; +}; + +module.exports.names = (params) => params.map(p => p.name).join(', '); + +module.exports['typed-params'] = (params) => { + return params.map(p => `${p.type}${p.name ? ' ' + p.name : ''}`).join(', '); +}; + +const slug = module.exports.slug = (str) => { + if (str === undefined) { + throw new Error('Missing argument'); + } + return str.replace(/\W/g, '-'); +}; + +const linksCache = new WeakMap(); + +function getAllLinks (items) { + if (linksCache.has(items)) { + return linksCache.get(items); + } + const res = {}; + linksCache.set(items, res); + for (const item of items) { + res[`xref-${item.anchor}`] = `xref:${item.__item_context.page}#${item.anchor}`; + res[slug(item.fullName)] = `pass:normal[xref:${item.__item_context.page}#${item.anchor}[\`${item.fullName}\`]]`; + } + return res; +} + +module.exports['with-prelude'] = (opts) => { + const links = getAllLinks(opts.data.site.items); + const contents = opts.fn(); + const neededLinks = contents + .match(/\{[-._a-z0-9]+\}/ig) + .map(m => m.replace(/^\{(.+)\}$/, '$1')) + .filter(k => k in links); + const prelude = neededLinks.map(k => `:${k}: ${links[k]}`).join('\n'); + return prelude + '\n' + contents; +}; diff --git a/lib/openzeppelin-contracts/docs/templates/page.hbs b/lib/openzeppelin-contracts/docs/templates/page.hbs new file mode 100644 index 0000000..cab050a --- /dev/null +++ b/lib/openzeppelin-contracts/docs/templates/page.hbs @@ -0,0 +1,4 @@ +:github-icon: pass:[] +{{#with-prelude}} +{{readme (readme-path)}} +{{/with-prelude}} diff --git a/lib/openzeppelin-contracts/docs/templates/properties.js b/lib/openzeppelin-contracts/docs/templates/properties.js new file mode 100644 index 0000000..5dde3a4 --- /dev/null +++ b/lib/openzeppelin-contracts/docs/templates/properties.js @@ -0,0 +1,49 @@ +const { isNodeType } = require('solidity-ast/utils'); +const { slug } = require('./helpers'); + +module.exports.anchor = function anchor ({ item, contract }) { + let res = ''; + if (contract) { + res += contract.name + '-'; + } + res += item.name; + if ('parameters' in item) { + const signature = item.parameters.parameters.map(v => v.typeName.typeDescriptions.typeString).join(','); + res += slug('(' + signature + ')'); + } + if (isNodeType('VariableDeclaration', item)) { + res += '-' + slug(item.typeName.typeDescriptions.typeString); + } + return res; +}; + +module.exports.inheritance = function ({ item, build }) { + if (!isNodeType('ContractDefinition', item)) { + throw new Error('used inherited-items on non-contract'); + } + + return item.linearizedBaseContracts + .map(id => build.deref('ContractDefinition', id)) + .filter((c, i) => c.name !== 'Context' || i === 0); +}; + +module.exports['has-functions'] = function ({ item }) { + return item.inheritance.some(c => c.functions.length > 0); +}; + +module.exports['has-events'] = function ({ item }) { + return item.inheritance.some(c => c.events.length > 0); +}; + +module.exports['inherited-functions'] = function ({ item }) { + const { inheritance } = item; + const baseFunctions = new Set( + inheritance.flatMap(c => c.functions.flatMap(f => f.baseFunctions ?? [])), + ); + return inheritance.map((contract, i) => ({ + contract, + functions: contract.functions.filter(f => + !baseFunctions.has(f.id) && (f.name !== 'constructor' || i === 0), + ), + })); +}; diff --git a/lib/openzeppelin-contracts/foundry.toml b/lib/openzeppelin-contracts/foundry.toml new file mode 100644 index 0000000..c0da487 --- /dev/null +++ b/lib/openzeppelin-contracts/foundry.toml @@ -0,0 +1,3 @@ +[fuzz] +runs = 10000 +max_test_rejects = 100000 diff --git a/lib/openzeppelin-contracts/hardhat.config.js b/lib/openzeppelin-contracts/hardhat.config.js new file mode 100644 index 0000000..4dbff0e --- /dev/null +++ b/lib/openzeppelin-contracts/hardhat.config.js @@ -0,0 +1,106 @@ +/// ENVVAR +// - CI: output gas report to file instead of stdout +// - COVERAGE: enable coverage report +// - ENABLE_GAS_REPORT: enable gas report +// - COMPILE_MODE: production modes enables optimizations (default: development) +// - COMPILE_VERSION: compiler version (default: 0.8.9) +// - COINMARKETCAP: coinmarkercat api key for USD value in gas report + +const fs = require('fs'); +const path = require('path'); +const argv = require('yargs/yargs')() + .env('') + .options({ + coverage: { + type: 'boolean', + default: false, + }, + gas: { + alias: 'enableGasReport', + type: 'boolean', + default: false, + }, + gasReport: { + alias: 'enableGasReportPath', + type: 'string', + implies: 'gas', + default: undefined, + }, + mode: { + alias: 'compileMode', + type: 'string', + choices: [ 'production', 'development' ], + default: 'development', + }, + ir: { + alias: 'enableIR', + type: 'boolean', + default: false, + }, + compiler: { + alias: 'compileVersion', + type: 'string', + default: '0.8.13', + }, + coinmarketcap: { + alias: 'coinmarketcapApiKey', + type: 'string', + }, + }) + .argv; + +require('@nomiclabs/hardhat-truffle5'); +require('hardhat-ignore-warnings'); + +require('solidity-docgen'); + +if (argv.gas) { + require('hardhat-gas-reporter'); +} + +for (const f of fs.readdirSync(path.join(__dirname, 'hardhat'))) { + require(path.join(__dirname, 'hardhat', f)); +} + +const withOptimizations = argv.gas || argv.compileMode === 'production'; + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +module.exports = { + solidity: { + version: argv.compiler, + settings: { + optimizer: { + enabled: withOptimizations, + runs: 200, + }, + viaIR: withOptimizations && argv.ir, + }, + }, + warnings: { + '*': { + 'code-size': withOptimizations, + 'unused-param': !argv.coverage, // coverage causes unused-param warnings + default: 'error', + }, + }, + networks: { + hardhat: { + blockGasLimit: 10000000, + allowUnlimitedContractSize: !withOptimizations, + }, + }, + gasReporter: { + showMethodSig: true, + currency: 'USD', + outputFile: argv.gasReport, + coinmarketcap: argv.coinmarketcap, + }, + docgen: require('./docs/config'), +}; + +if (argv.coverage) { + require('solidity-coverage'); + module.exports.networks.hardhat.initialBaseFeePerGas = 0; +} diff --git a/lib/openzeppelin-contracts/hardhat/env-contract.js b/lib/openzeppelin-contracts/hardhat/env-contract.js new file mode 100644 index 0000000..74d54cf --- /dev/null +++ b/lib/openzeppelin-contracts/hardhat/env-contract.js @@ -0,0 +1,10 @@ +extendEnvironment(env => { + const { contract } = env; + + env.contract = function (name, body) { + // remove the default account from the accounts list used in tests, in order + // to protect tests against accidentally passing due to the contract + // deployer being used subsequently as function caller + contract(name, accounts => body(accounts.slice(1))); + }; +}); diff --git a/lib/openzeppelin-contracts/hardhat/ignore-unreachable-warnings.js b/lib/openzeppelin-contracts/hardhat/ignore-unreachable-warnings.js new file mode 100644 index 0000000..c9d3c36 --- /dev/null +++ b/lib/openzeppelin-contracts/hardhat/ignore-unreachable-warnings.js @@ -0,0 +1,47 @@ +// Warnings about unreachable code are emitted with a source location that corresponds to the unreachable code. +// We have some testing contracts that purposely cause unreachable code, but said code is in the library contracts, and +// with hardhat-ignore-warnings we are not able to selectively ignore them without potentially ignoring relevant +// warnings that we don't want to miss. +// Thus, we need to handle these warnings separately. We force Hardhat to compile them in a separate compilation job and +// then ignore the warnings about unreachable code that come from that compilation job. + +const { task } = require('hardhat/config'); +const { + TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE, + TASK_COMPILE_SOLIDITY_COMPILE, +} = require('hardhat/builtin-tasks/task-names'); + +const marker = Symbol('unreachable'); +const markedCache = new WeakMap(); + +task(TASK_COMPILE_SOLIDITY_GET_COMPILATION_JOB_FOR_FILE, async (params, _, runSuper) => { + const job = await runSuper(params); + // If the file is in the unreachable directory, we make a copy of the config and mark it, which will cause it to get + // compiled separately (along with the other marked files). + if (params.file.sourceName.startsWith('contracts/mocks/') && /\bunreachable\b/.test(params.file.sourceName)) { + const originalConfig = job.solidityConfig; + let markedConfig = markedCache.get(originalConfig); + if (markedConfig === undefined) { + markedConfig = { ...originalConfig, [marker]: true }; + markedCache.set(originalConfig, markedConfig); + } + job.solidityConfig = markedConfig; + } + return job; +}); + +const W_UNREACHABLE_CODE = '5740'; + +task(TASK_COMPILE_SOLIDITY_COMPILE, async (params, _, runSuper) => { + const marked = params.compilationJob.solidityConfig[marker]; + const result = await runSuper(params); + if (marked) { + result.output = { + ...result.output, + errors: result.output.errors?.filter( + e => e.severity !== 'warning' || e.errorCode !== W_UNREACHABLE_CODE, + ), + }; + } + return result; +}); diff --git a/lib/openzeppelin-contracts/hardhat/skip-foundry-tests.js b/lib/openzeppelin-contracts/hardhat/skip-foundry-tests.js new file mode 100644 index 0000000..b803028 --- /dev/null +++ b/lib/openzeppelin-contracts/hardhat/skip-foundry-tests.js @@ -0,0 +1,7 @@ +const { subtask } = require('hardhat/config'); +const { TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } = require('hardhat/builtin-tasks/task-names'); + +subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS) + .setAction(async (_, __, runSuper) => + (await runSuper()).filter((path) => !path.endsWith('.t.sol')), + ); diff --git a/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.prop.sol b/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.prop.sol new file mode 100644 index 0000000..c34512b --- /dev/null +++ b/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.prop.sol @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import "forge-std/Test.sol"; + +// TODO: use interface provided by forge-std v1.0.0 or later +// import {IERC20} from "forge-std/interfaces/IERC20.sol"; +interface IERC20 { + event Transfer(address indexed from, address indexed to, uint value); + event Approval(address indexed owner, address indexed spender, uint value); + function totalSupply() external view returns (uint); + function balanceOf(address account) external view returns (uint); + function transfer(address to, uint amount) external returns (bool); + function allowance(address owner, address spender) external view returns (uint); + function approve(address spender, uint amount) external returns (bool); + function transferFrom(address from, address to, uint amount) external returns (bool); +} + +// TODO: use interface provided by forge-std v1.0.0 or later +// import {IERC4626} from "forge-std/interfaces/IERC4626.sol"; +interface IERC4626 is IERC20 { + event Deposit(address indexed caller, address indexed owner, uint assets, uint shares); + event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint assets, uint shares); + function asset() external view returns (address assetTokenAddress); + function totalAssets() external view returns (uint totalManagedAssets); + function convertToShares(uint assets) external view returns (uint shares); + function convertToAssets(uint shares) external view returns (uint assets); + function maxDeposit(address receiver) external view returns (uint maxAssets); + function previewDeposit(uint assets) external view returns (uint shares); + function deposit(uint assets, address receiver) external returns (uint shares); + function maxMint(address receiver) external view returns (uint maxShares); + function previewMint(uint shares) external view returns (uint assets); + function mint(uint shares, address receiver) external returns (uint assets); + function maxWithdraw(address owner) external view returns (uint maxAssets); + function previewWithdraw(uint assets) external view returns (uint shares); + function withdraw(uint assets, address receiver, address owner) external returns (uint shares); + function maxRedeem(address owner) external view returns (uint maxShares); + function previewRedeem(uint shares) external view returns (uint assets); + function redeem(uint shares, address receiver, address owner) external returns (uint assets); +} + +abstract contract ERC4626Prop is Test { + uint internal _delta_; + + address internal _underlying_; + address internal _vault_; + + bool internal _vaultMayBeEmpty; + bool internal _unlimitedAmount; + + // + // asset + // + + // asset + // "MUST NOT revert." + function prop_asset(address caller) public { + vm.prank(caller); IERC4626(_vault_).asset(); + } + + // totalAssets + // "MUST NOT revert." + function prop_totalAssets(address caller) public { + vm.prank(caller); IERC4626(_vault_).totalAssets(); + } + + // + // convert + // + + // convertToShares + // "MUST NOT show any variations depending on the caller." + function prop_convertToShares(address caller1, address caller2, uint assets) public { + vm.prank(caller1); uint res1 = vault_convertToShares(assets); // "MAY revert due to integer overflow caused by an unreasonably large input." + vm.prank(caller2); uint res2 = vault_convertToShares(assets); // "MAY revert due to integer overflow caused by an unreasonably large input." + assertEq(res1, res2); + } + + // convertToAssets + // "MUST NOT show any variations depending on the caller." + function prop_convertToAssets(address caller1, address caller2, uint shares) public { + vm.prank(caller1); uint res1 = vault_convertToAssets(shares); // "MAY revert due to integer overflow caused by an unreasonably large input." + vm.prank(caller2); uint res2 = vault_convertToAssets(shares); // "MAY revert due to integer overflow caused by an unreasonably large input." + assertEq(res1, res2); + } + + // + // deposit + // + + // maxDeposit + // "MUST NOT revert." + function prop_maxDeposit(address caller, address receiver) public { + vm.prank(caller); IERC4626(_vault_).maxDeposit(receiver); + } + + // previewDeposit + // "MUST return as close to and no more than the exact amount of Vault + // shares that would be minted in a deposit call in the same transaction. + // I.e. deposit should return the same or more shares as previewDeposit if + // called in the same transaction." + function prop_previewDeposit(address caller, address receiver, address other, uint assets) public { + vm.prank(other); uint sharesPreview = vault_previewDeposit(assets); // "MAY revert due to other conditions that would also cause deposit to revert." + vm.prank(caller); uint sharesActual = vault_deposit(assets, receiver); + assertApproxGeAbs(sharesActual, sharesPreview, _delta_); + } + + // deposit + function prop_deposit(address caller, address receiver, uint assets) public { + uint oldCallerAsset = IERC20(_underlying_).balanceOf(caller); + uint oldReceiverShare = IERC20(_vault_).balanceOf(receiver); + uint oldAllowance = IERC20(_underlying_).allowance(caller, _vault_); + + vm.prank(caller); uint shares = vault_deposit(assets, receiver); + + uint newCallerAsset = IERC20(_underlying_).balanceOf(caller); + uint newReceiverShare = IERC20(_vault_).balanceOf(receiver); + uint newAllowance = IERC20(_underlying_).allowance(caller, _vault_); + + assertApproxEqAbs(newCallerAsset, oldCallerAsset - assets, _delta_, "asset"); // NOTE: this may fail if the caller is a contract in which the asset is stored + assertApproxEqAbs(newReceiverShare, oldReceiverShare + shares, _delta_, "share"); + if (oldAllowance != type(uint).max) assertApproxEqAbs(newAllowance, oldAllowance - assets, _delta_, "allowance"); + } + + // + // mint + // + + // maxMint + // "MUST NOT revert." + function prop_maxMint(address caller, address receiver) public { + vm.prank(caller); IERC4626(_vault_).maxMint(receiver); + } + + // previewMint + // "MUST return as close to and no fewer than the exact amount of assets + // that would be deposited in a mint call in the same transaction. I.e. mint + // should return the same or fewer assets as previewMint if called in the + // same transaction." + function prop_previewMint(address caller, address receiver, address other, uint shares) public { + vm.prank(other); uint assetsPreview = vault_previewMint(shares); + vm.prank(caller); uint assetsActual = vault_mint(shares, receiver); + assertApproxLeAbs(assetsActual, assetsPreview, _delta_); + } + + // mint + function prop_mint(address caller, address receiver, uint shares) public { + uint oldCallerAsset = IERC20(_underlying_).balanceOf(caller); + uint oldReceiverShare = IERC20(_vault_).balanceOf(receiver); + uint oldAllowance = IERC20(_underlying_).allowance(caller, _vault_); + + vm.prank(caller); uint assets = vault_mint(shares, receiver); + + uint newCallerAsset = IERC20(_underlying_).balanceOf(caller); + uint newReceiverShare = IERC20(_vault_).balanceOf(receiver); + uint newAllowance = IERC20(_underlying_).allowance(caller, _vault_); + + assertApproxEqAbs(newCallerAsset, oldCallerAsset - assets, _delta_, "asset"); // NOTE: this may fail if the caller is a contract in which the asset is stored + assertApproxEqAbs(newReceiverShare, oldReceiverShare + shares, _delta_, "share"); + if (oldAllowance != type(uint).max) assertApproxEqAbs(newAllowance, oldAllowance - assets, _delta_, "allowance"); + } + + // + // withdraw + // + + // maxWithdraw + // "MUST NOT revert." + // NOTE: some implementations failed due to arithmetic overflow + function prop_maxWithdraw(address caller, address owner) public { + vm.prank(caller); IERC4626(_vault_).maxWithdraw(owner); + } + + // previewWithdraw + // "MUST return as close to and no fewer than the exact amount of Vault + // shares that would be burned in a withdraw call in the same transaction. + // I.e. withdraw should return the same or fewer shares as previewWithdraw + // if called in the same transaction." + function prop_previewWithdraw(address caller, address receiver, address owner, address other, uint assets) public { + vm.prank(other); uint preview = vault_previewWithdraw(assets); + vm.prank(caller); uint actual = vault_withdraw(assets, receiver, owner); + assertApproxLeAbs(actual, preview, _delta_); + } + + // withdraw + function prop_withdraw(address caller, address receiver, address owner, uint assets) public { + uint oldReceiverAsset = IERC20(_underlying_).balanceOf(receiver); + uint oldOwnerShare = IERC20(_vault_).balanceOf(owner); + uint oldAllowance = IERC20(_vault_).allowance(owner, caller); + + vm.prank(caller); uint shares = vault_withdraw(assets, receiver, owner); + + uint newReceiverAsset = IERC20(_underlying_).balanceOf(receiver); + uint newOwnerShare = IERC20(_vault_).balanceOf(owner); + uint newAllowance = IERC20(_vault_).allowance(owner, caller); + + assertApproxEqAbs(newOwnerShare, oldOwnerShare - shares, _delta_, "share"); + assertApproxEqAbs(newReceiverAsset, oldReceiverAsset + assets, _delta_, "asset"); // NOTE: this may fail if the receiver is a contract in which the asset is stored + if (caller != owner && oldAllowance != type(uint).max) assertApproxEqAbs(newAllowance, oldAllowance - shares, _delta_, "allowance"); + + assertTrue(caller == owner || oldAllowance != 0 || (shares == 0 && assets == 0), "access control"); + } + + // + // redeem + // + + // maxRedeem + // "MUST NOT revert." + function prop_maxRedeem(address caller, address owner) public { + vm.prank(caller); IERC4626(_vault_).maxRedeem(owner); + } + + // previewRedeem + // "MUST return as close to and no more than the exact amount of assets that + // would be withdrawn in a redeem call in the same transaction. I.e. redeem + // should return the same or more assets as previewRedeem if called in the + // same transaction." + function prop_previewRedeem(address caller, address receiver, address owner, address other, uint shares) public { + vm.prank(other); uint preview = vault_previewRedeem(shares); + vm.prank(caller); uint actual = vault_redeem(shares, receiver, owner); + assertApproxGeAbs(actual, preview, _delta_); + } + + // redeem + function prop_redeem(address caller, address receiver, address owner, uint shares) public { + uint oldReceiverAsset = IERC20(_underlying_).balanceOf(receiver); + uint oldOwnerShare = IERC20(_vault_).balanceOf(owner); + uint oldAllowance = IERC20(_vault_).allowance(owner, caller); + + vm.prank(caller); uint assets = vault_redeem(shares, receiver, owner); + + uint newReceiverAsset = IERC20(_underlying_).balanceOf(receiver); + uint newOwnerShare = IERC20(_vault_).balanceOf(owner); + uint newAllowance = IERC20(_vault_).allowance(owner, caller); + + assertApproxEqAbs(newOwnerShare, oldOwnerShare - shares, _delta_, "share"); + assertApproxEqAbs(newReceiverAsset, oldReceiverAsset + assets, _delta_, "asset"); // NOTE: this may fail if the receiver is a contract in which the asset is stored + if (caller != owner && oldAllowance != type(uint).max) assertApproxEqAbs(newAllowance, oldAllowance - shares, _delta_, "allowance"); + + assertTrue(caller == owner || oldAllowance != 0 || (shares == 0 && assets == 0), "access control"); + } + + // + // round trip properties + // + + // redeem(deposit(a)) <= a + function prop_RT_deposit_redeem(address caller, uint assets) public { + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint shares = vault_deposit(assets, caller); + vm.prank(caller); uint assets2 = vault_redeem(shares, caller, caller); + assertApproxLeAbs(assets2, assets, _delta_); + } + + // s = deposit(a) + // s' = withdraw(a) + // s' >= s + function prop_RT_deposit_withdraw(address caller, uint assets) public { + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint shares1 = vault_deposit(assets, caller); + vm.prank(caller); uint shares2 = vault_withdraw(assets, caller, caller); + assertApproxGeAbs(shares2, shares1, _delta_); + } + + // deposit(redeem(s)) <= s + function prop_RT_redeem_deposit(address caller, uint shares) public { + vm.prank(caller); uint assets = vault_redeem(shares, caller, caller); + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint shares2 = vault_deposit(assets, caller); + assertApproxLeAbs(shares2, shares, _delta_); + } + + // a = redeem(s) + // a' = mint(s) + // a' >= a + function prop_RT_redeem_mint(address caller, uint shares) public { + vm.prank(caller); uint assets1 = vault_redeem(shares, caller, caller); + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint assets2 = vault_mint(shares, caller); + assertApproxGeAbs(assets2, assets1, _delta_); + } + + // withdraw(mint(s)) >= s + function prop_RT_mint_withdraw(address caller, uint shares) public { + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint assets = vault_mint(shares, caller); + vm.prank(caller); uint shares2 = vault_withdraw(assets, caller, caller); + assertApproxGeAbs(shares2, shares, _delta_); + } + + // a = mint(s) + // a' = redeem(s) + // a' <= a + function prop_RT_mint_redeem(address caller, uint shares) public { + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint assets1 = vault_mint(shares, caller); + vm.prank(caller); uint assets2 = vault_redeem(shares, caller, caller); + assertApproxLeAbs(assets2, assets1, _delta_); + } + + // mint(withdraw(a)) >= a + function prop_RT_withdraw_mint(address caller, uint assets) public { + vm.prank(caller); uint shares = vault_withdraw(assets, caller, caller); + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint assets2 = vault_mint(shares, caller); + assertApproxGeAbs(assets2, assets, _delta_); + } + + // s = withdraw(a) + // s' = deposit(a) + // s' <= s + function prop_RT_withdraw_deposit(address caller, uint assets) public { + vm.prank(caller); uint shares1 = vault_withdraw(assets, caller, caller); + if (!_vaultMayBeEmpty) vm.assume(IERC20(_vault_).totalSupply() > 0); + vm.prank(caller); uint shares2 = vault_deposit(assets, caller); + assertApproxLeAbs(shares2, shares1, _delta_); + } + + // + // utils + // + + function vault_convertToShares(uint assets) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.convertToShares.selector, assets)); + } + function vault_convertToAssets(uint shares) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.convertToAssets.selector, shares)); + } + + function vault_maxDeposit(address receiver) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.maxDeposit.selector, receiver)); + } + function vault_maxMint(address receiver) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.maxMint.selector, receiver)); + } + function vault_maxWithdraw(address owner) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.maxWithdraw.selector, owner)); + } + function vault_maxRedeem(address owner) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.maxRedeem.selector, owner)); + } + + function vault_previewDeposit(uint assets) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.previewDeposit.selector, assets)); + } + function vault_previewMint(uint shares) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.previewMint.selector, shares)); + } + function vault_previewWithdraw(uint assets) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.previewWithdraw.selector, assets)); + } + function vault_previewRedeem(uint shares) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.previewRedeem.selector, shares)); + } + + function vault_deposit(uint assets, address receiver) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.deposit.selector, assets, receiver)); + } + function vault_mint(uint shares, address receiver) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.mint.selector, shares, receiver)); + } + function vault_withdraw(uint assets, address receiver, address owner) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.withdraw.selector, assets, receiver, owner)); + } + function vault_redeem(uint shares, address receiver, address owner) internal returns (uint) { + return _call_vault(abi.encodeWithSelector(IERC4626.redeem.selector, shares, receiver, owner)); + } + + function _call_vault(bytes memory data) internal returns (uint) { + (bool success, bytes memory retdata) = _vault_.call(data); + if (success) return abi.decode(retdata, (uint)); + vm.assume(false); // if reverted, discard the current fuzz inputs, and let the fuzzer to start a new fuzz run + return 0; // silence warning + } + + function assertApproxGeAbs(uint a, uint b, uint maxDelta) internal { + if (!(a >= b)) { + uint dt = b - a; + if (dt > maxDelta) { + emit log ("Error: a >=~ b not satisfied [uint]"); + emit log_named_uint (" Value a", a); + emit log_named_uint (" Value b", b); + emit log_named_uint (" Max Delta", maxDelta); + emit log_named_uint (" Delta", dt); + fail(); + } + } + } + + function assertApproxLeAbs(uint a, uint b, uint maxDelta) internal { + if (!(a <= b)) { + uint dt = a - b; + if (dt > maxDelta) { + emit log ("Error: a <=~ b not satisfied [uint]"); + emit log_named_uint (" Value a", a); + emit log_named_uint (" Value b", b); + emit log_named_uint (" Max Delta", maxDelta); + emit log_named_uint (" Delta", dt); + fail(); + } + } + } +} diff --git a/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.test.sol b/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.test.sol new file mode 100644 index 0000000..6254a05 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/erc4626-tests/ERC4626.test.sol @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import "./ERC4626.prop.sol"; + +interface IMockERC20 is IERC20 { + function mint(address to, uint value) external; + function burn(address from, uint value) external; +} + +abstract contract ERC4626Test is ERC4626Prop { + function setUp() public virtual; + + uint constant N = 4; + + struct Init { + address[N] user; + uint[N] share; + uint[N] asset; + int yield; + } + + // setup initial vault state as follows: + // + // totalAssets == sum(init.share) + init.yield + // totalShares == sum(init.share) + // + // init.user[i]'s assets == init.asset[i] + // init.user[i]'s shares == init.share[i] + function setUpVault(Init memory init) public virtual { + // setup initial shares and assets for individual users + for (uint i = 0; i < N; i++) { + address user = init.user[i]; + vm.assume(_isEOA(user)); + // shares + uint shares = init.share[i]; + try IMockERC20(_underlying_).mint(user, shares) {} catch { vm.assume(false); } + _approve(_underlying_, user, _vault_, shares); + vm.prank(user); try IERC4626(_vault_).deposit(shares, user) {} catch { vm.assume(false); } + // assets + uint assets = init.asset[i]; + try IMockERC20(_underlying_).mint(user, assets) {} catch { vm.assume(false); } + } + + // setup initial yield for vault + setUpYield(init); + } + + // setup initial yield + function setUpYield(Init memory init) public virtual { + if (init.yield >= 0) { // gain + uint gain = uint(init.yield); + try IMockERC20(_underlying_).mint(_vault_, gain) {} catch { vm.assume(false); } // this can be replaced by calling yield generating functions if provided by the vault + } else { // loss + vm.assume(init.yield > type(int).min); // avoid overflow in conversion + uint loss = uint(-1 * init.yield); + try IMockERC20(_underlying_).burn(_vault_, loss) {} catch { vm.assume(false); } // this can be replaced by calling yield generating functions if provided by the vault + } + } + + // + // asset + // + + function test_asset(Init memory init) public virtual { + setUpVault(init); + address caller = init.user[0]; + prop_asset(caller); + } + + function test_totalAssets(Init memory init) public virtual { + setUpVault(init); + address caller = init.user[0]; + prop_totalAssets(caller); + } + + // + // convert + // + + function test_convertToShares(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller1 = init.user[0]; + address caller2 = init.user[1]; + prop_convertToShares(caller1, caller2, assets); + } + + function test_convertToAssets(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller1 = init.user[0]; + address caller2 = init.user[1]; + prop_convertToAssets(caller1, caller2, shares); + } + + // + // deposit + // + + function test_maxDeposit(Init memory init) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + prop_maxDeposit(caller, receiver); + } + + function test_previewDeposit(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address other = init.user[2]; + assets = bound(assets, 0, _max_deposit(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_previewDeposit(caller, receiver, other, assets); + } + + function test_deposit(Init memory init, uint assets, uint allowance) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + assets = bound(assets, 0, _max_deposit(caller)); + _approve(_underlying_, caller, _vault_, allowance); + prop_deposit(caller, receiver, assets); + } + + // + // mint + // + + function test_maxMint(Init memory init) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + prop_maxMint(caller, receiver); + } + + function test_previewMint(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address other = init.user[2]; + shares = bound(shares, 0, _max_mint(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_previewMint(caller, receiver, other, shares); + } + + function test_mint(Init memory init, uint shares, uint allowance) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + shares = bound(shares, 0, _max_mint(caller)); + _approve(_underlying_, caller, _vault_, allowance); + prop_mint(caller, receiver, shares); + } + + // + // withdraw + // + + function test_maxWithdraw(Init memory init) public virtual { + setUpVault(init); + address caller = init.user[0]; + address owner = init.user[1]; + prop_maxWithdraw(caller, owner); + } + + function test_previewWithdraw(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address owner = init.user[2]; + address other = init.user[3]; + assets = bound(assets, 0, _max_withdraw(owner)); + _approve(_vault_, owner, caller, type(uint).max); + prop_previewWithdraw(caller, receiver, owner, other, assets); + } + + function test_withdraw(Init memory init, uint assets, uint allowance) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address owner = init.user[2]; + assets = bound(assets, 0, _max_withdraw(owner)); + _approve(_vault_, owner, caller, allowance); + prop_withdraw(caller, receiver, owner, assets); + } + + function testFail_withdraw(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address owner = init.user[2]; + assets = bound(assets, 0, _max_withdraw(owner)); + vm.assume(caller != owner); + vm.assume(assets > 0); + _approve(_vault_, owner, caller, 0); + vm.prank(caller); uint shares = IERC4626(_vault_).withdraw(assets, receiver, owner); + assertGt(shares, 0); // this assert is expected to fail + } + + // + // redeem + // + + function test_maxRedeem(Init memory init) public virtual { + setUpVault(init); + address caller = init.user[0]; + address owner = init.user[1]; + prop_maxRedeem(caller, owner); + } + + function test_previewRedeem(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address owner = init.user[2]; + address other = init.user[3]; + shares = bound(shares, 0, _max_redeem(owner)); + _approve(_vault_, owner, caller, type(uint).max); + prop_previewRedeem(caller, receiver, owner, other, shares); + } + + function test_redeem(Init memory init, uint shares, uint allowance) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address owner = init.user[2]; + shares = bound(shares, 0, _max_redeem(owner)); + _approve(_vault_, owner, caller, allowance); + prop_redeem(caller, receiver, owner, shares); + } + + function testFail_redeem(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + address receiver = init.user[1]; + address owner = init.user[2]; + shares = bound(shares, 0, _max_redeem(owner)); + vm.assume(caller != owner); + vm.assume(shares > 0); + _approve(_vault_, owner, caller, 0); + vm.prank(caller); IERC4626(_vault_).redeem(shares, receiver, owner); + } + + // + // round trip tests + // + + function test_RT_deposit_redeem(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + assets = bound(assets, 0, _max_deposit(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_deposit_redeem(caller, assets); + } + + function test_RT_deposit_withdraw(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + assets = bound(assets, 0, _max_deposit(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_deposit_withdraw(caller, assets); + } + + function test_RT_redeem_deposit(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + shares = bound(shares, 0, _max_redeem(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_redeem_deposit(caller, shares); + } + + function test_RT_redeem_mint(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + shares = bound(shares, 0, _max_redeem(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_redeem_mint(caller, shares); + } + + function test_RT_mint_withdraw(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + shares = bound(shares, 0, _max_mint(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_mint_withdraw(caller, shares); + } + + function test_RT_mint_redeem(Init memory init, uint shares) public virtual { + setUpVault(init); + address caller = init.user[0]; + shares = bound(shares, 0, _max_mint(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_mint_redeem(caller, shares); + } + + function test_RT_withdraw_mint(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + assets = bound(assets, 0, _max_withdraw(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_withdraw_mint(caller, assets); + } + + function test_RT_withdraw_deposit(Init memory init, uint assets) public virtual { + setUpVault(init); + address caller = init.user[0]; + assets = bound(assets, 0, _max_withdraw(caller)); + _approve(_underlying_, caller, _vault_, type(uint).max); + prop_RT_withdraw_deposit(caller, assets); + } + + // + // utils + // + + function _isContract(address account) internal view returns (bool) { return account.code.length > 0; } + function _isEOA (address account) internal view returns (bool) { return account.code.length == 0; } + + function _approve(address token, address owner, address spender, uint amount) internal { + vm.prank(owner); _safeApprove(token, spender, 0); + vm.prank(owner); _safeApprove(token, spender, amount); + } + + function _safeApprove(address token, address spender, uint amount) internal { + (bool success, bytes memory retdata) = token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, amount)); + vm.assume(success); + if (retdata.length > 0) vm.assume(abi.decode(retdata, (bool))); + } + + function _max_deposit(address from) internal virtual returns (uint) { + if (_unlimitedAmount) return type(uint).max; + return IERC20(_underlying_).balanceOf(from); + } + + function _max_mint(address from) internal virtual returns (uint) { + if (_unlimitedAmount) return type(uint).max; + return vault_convertToShares(IERC20(_underlying_).balanceOf(from)); + } + + function _max_withdraw(address from) internal virtual returns (uint) { + if (_unlimitedAmount) return type(uint).max; + return vault_convertToAssets(IERC20(_vault_).balanceOf(from)); // may be different from maxWithdraw(from) + } + + function _max_redeem(address from) internal virtual returns (uint) { + if (_unlimitedAmount) return type(uint).max; + return IERC20(_vault_).balanceOf(from); // may be different from maxRedeem(from) + } +} diff --git a/lib/openzeppelin-contracts/lib/erc4626-tests/LICENSE b/lib/openzeppelin-contracts/lib/erc4626-tests/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/lib/openzeppelin-contracts/lib/erc4626-tests/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. diff --git a/lib/openzeppelin-contracts/lib/erc4626-tests/README.md b/lib/openzeppelin-contracts/lib/erc4626-tests/README.md new file mode 100644 index 0000000..651e443 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/erc4626-tests/README.md @@ -0,0 +1,116 @@ +# ERC4626 Property Tests + +Foundry (dapptools-style) property-based tests for [ERC4626] standard conformance. + +[ERC4626]: + +You can read our post on "_[Generalized property tests for ERC4626 vaults][post]_." + +[post]: + +## Overview + +#### What is it? +- Test suites for checking if the given ERC4626 implementation satisfies the **standard requirements**. +- Dapptools-style **property-based tests** for fuzzing or symbolic execution testing. +- Tests that are **independent** from implementation details, thus applicable for any ERC4626 vaults. + +#### What isn’t it? +- It does NOT test implementation-specific details, e.g., how to generate and distribute yields, how to compute the share price, etc. + +#### Testing properties: + +- **Round-trip properties**: no one can make a free profit by depositing and immediately withdrawing back and forth. + +- **Functional correctness**: the `deposit()`, `mint()`, `withdraw()`, and `redeem()` functions update the balance and allowance properly. + +- The `preview{Deposit,Redeem}()` functions **MUST NOT over-estimate** the exact amount.[^1] + +[^1]: That is, the `deposit()` and `redeem()` functions “MUST return the same or more amounts as their preview function if called in the same transaction.” + +- The `preview{Mint,Withdraw}()` functions **MUST NOT under-estimate** the exact amount.[^2] + +[^2]: That is, the `mint()` and `withdraw()` functions “MUST return the same or fewer amounts as their preview function if called in the same transaction.” + +- The `convertTo{Shares,Assets}` functions “**MUST NOT show any variations** depending on the caller.” + +- The `asset()`, `totalAssets()`, and `max{Deposit,Mint,Withdraw,Redeem}()` functions “**MUST NOT revert**.” + +## Usage + +**Step 0**: Install [foundry] and add [forge-std] in your vault repo: +```bash +$ curl -L https://foundry.paradigm.xyz | bash + +$ cd /path/to/your-erc4626-vault +$ forge install foundry-rs/forge-std +``` + +[foundry]: +[forge-std]: + +**Step 1**: Add this [erc4626-tests] as a dependency to your vault: +```bash +$ cd /path/to/your-erc4626-vault +$ forge install a16z/erc4626-tests +``` + +[erc4626-tests]: + +**Step 2**: Extend the abstract test contract [`ERC4626Test`](ERC4626.test.sol) with your own custom vault setup method, for example: + +```solidity +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity >=0.8.0 <0.9.0; + +import "erc4626-tests/ERC4626.test.sol"; + +import { ERC20Mock } from "/path/to/mocks/ERC20Mock.sol"; +import { ERC4626Mock } from "/path/to/mocks/ERC4626Mock.sol"; + +contract ERC4626StdTest is ERC4626Test { + function setUp() public override { + _underlying_ = address(new ERC20Mock("Mock ERC20", "MERC20", 18)); + _vault_ = address(new ERC4626Mock(ERC20Mock(__underlying__), "Mock ERC4626", "MERC4626")); + _delta_ = 0; + _vaultMayBeEmpty = false; + _unlimitedAmount = false; + } +} +``` + +Specifically, set the state variables as follows: +- `_vault_`: the address of your ERC4626 vault. +- `_underlying_`: the address of the underlying asset of your vault. Note that the default `setupVault()` and `setupYield()` methods of `ERC4626Test` assume that it implements `mint(address to, uint value)` and `burn(address from, uint value)`. You can override the setup methods with your own if such `mint()` and `burn()` are not implemented. +- `_delta_`: the maximum approximation error size to be passed to [`assertApproxEqAbs()`]. It must be given as an absolute value (not a percentage) in the smallest unit (e.g., Wei or Satoshi). Note that all the tests are expected to pass with `__delta__ == 0` as long as your vault follows the [preferred rounding direction] as specified in the standard. If your vault doesn't follow the preferred rounding direction, you can set `__delta__` to a reasonable size of rounding errors where the adversarial profit of exploiting such rounding errors stays sufficiently small compared to the gas cost. (You can read our [post] for more about the adversarial profit.) +- `_vaultMayBeEmpty`: when set to false, fuzz inputs that empties the vault are ignored. +- `_unlimitedAmount`: when set to false, fuzz inputs are restricted to the currently available amount from the caller. Limiting the amount can speed up fuzzing, but may miss some edge cases. + +[`assertApproxEqAbs()`]: + +[preferred rounding direction]: + +**Step 3**: Run `forge test` + +``` +$ forge test +``` + +## Examples + +Below are examples of adding these property tests to existing ERC4626 vaults: +- [OpenZeppelin ERC4626] [[diff](https://github.com/daejunpark/openzeppelin-contracts/pull/1/files)] +- [Solmate ERC4626] [[diff](https://github.com/daejunpark/solmate/pull/1/files)] +- [Revenue Distribution Token] [[diff](https://github.com/daejunpark/revenue-distribution-token/pull/1/files)] +- [Yield Daddy ERC4626 wrappers] [[diff](https://github.com/daejunpark/yield-daddy/pull/1/files)][^bug] + +[OpenZeppelin ERC4626]: +[Solmate ERC4626]: +[Revenue Distribution Token]: +[Yield Daddy ERC4626 wrappers]: + +[^bug]: Our property tests indeed revealed an [issue](https://github.com/timeless-fi/yield-daddy/issues/7) in their eToken testing mock contract. The tests passed after it is [fixed](https://github.com/daejunpark/yield-daddy/commit/721cf4bd766805fd409455434aa5fd1a9b2df25c). + +## Disclaimer + +_These smart contracts are being provided as is. No guarantee, representation or warranty is being made, express or implied, as to the safety or correctness of the user interface or the smart contracts. They have not been audited and as such there can be no assurance they will work as intended, and users may experience delays, failures, errors, omissions or loss of transmitted information. THE SMART CONTRACTS CONTAINED HEREIN ARE FURNISHED AS IS, WHERE IS, WITH ALL FAULTS AND WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING ANY WARRANTY OF MERCHANTABILITY, NON-INFRINGEMENT OR FITNESS FOR ANY PARTICULAR PURPOSE. Further, use of any of these smart contracts may be restricted or prohibited under applicable law, including securities laws, and it is therefore strongly advised for you to contact a reputable attorney in any jurisdiction where these smart contracts may be accessible for any questions or concerns with respect thereto. Further, no information provided in this repo should be construed as investment advice or legal advice for any particular facts or circumstances, and is not meant to replace competent counsel. a16z is not liable for any use of the foregoing, and users should proceed with caution and use at their own risk. See a16z.com/disclosures for more info._ diff --git a/lib/openzeppelin-contracts/lib/forge-std/.github/workflows/tests.yml b/lib/openzeppelin-contracts/lib/forge-std/.github/workflows/tests.yml new file mode 100644 index 0000000..8e86b25 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/.github/workflows/tests.yml @@ -0,0 +1,27 @@ +name: Tests +on: [push, pull_request] + +jobs: + check: + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install dependencies + run: forge install + - name: Run tests + run: forge test -vvv + - name: Build Test with older solc versions + run: | + forge build --contracts src/Test.sol --use solc:0.8.0 + forge build --contracts src/Test.sol --use solc:0.7.6 + forge build --contracts src/Test.sol --use solc:0.7.0 + forge build --contracts src/Test.sol --use solc:0.6.0 diff --git a/lib/openzeppelin-contracts/lib/forge-std/.gitignore b/lib/openzeppelin-contracts/lib/forge-std/.gitignore new file mode 100644 index 0000000..999e4a7 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/.gitignore @@ -0,0 +1,4 @@ +cache/ +out/ +.vscode +.idea \ No newline at end of file diff --git a/lib/openzeppelin-contracts/lib/forge-std/.gitmodules b/lib/openzeppelin-contracts/lib/forge-std/.gitmodules new file mode 100644 index 0000000..e124719 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/ds-test"] + path = lib/ds-test + url = https://github.com/dapphub/ds-test diff --git a/lib/openzeppelin-contracts/lib/forge-std/LICENSE-APACHE b/lib/openzeppelin-contracts/lib/forge-std/LICENSE-APACHE new file mode 100644 index 0000000..6873d54 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/LICENSE-APACHE @@ -0,0 +1,203 @@ +Copyright (c) 2022 Brock Elmore + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/openzeppelin-contracts/lib/forge-std/LICENSE-MIT b/lib/openzeppelin-contracts/lib/forge-std/LICENSE-MIT new file mode 100644 index 0000000..851cd47 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2022 Brock Elmore + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER +DEALINGS IN THE SOFTWARE.R diff --git a/lib/openzeppelin-contracts/lib/forge-std/README.md b/lib/openzeppelin-contracts/lib/forge-std/README.md new file mode 100644 index 0000000..67dc160 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/README.md @@ -0,0 +1,246 @@ +# Forge Standard Library • [![tests](https://github.com/brockelmore/forge-std/actions/workflows/tests.yml/badge.svg)](https://github.com/brockelmore/forge-std/actions/workflows/tests.yml) + +Forge Standard Library is a collection of helpful contracts for use with [`forge` and `foundry`](https://github.com/foundry-rs/foundry). It leverages `forge`'s cheatcodes to make writing tests easier and faster, while improving the UX of cheatcodes. + +**Learn how to use Forge Std with the [📖 Foundry Book (Forge Std Guide)](https://book.getfoundry.sh/forge/forge-std.html).** + +## Install + +```bash +forge install foundry-rs/forge-std +``` + +## Contracts +### stdError + +This is a helper contract for errors and reverts. In `forge`, this contract is particularly helpful for the `expectRevert` cheatcode, as it provides all compiler builtin errors. + +See the contract itself for all error codes. + +#### Example usage + +```solidity + +import "forge-std/Test.sol"; + +contract TestContract is Test { + ErrorsTest test; + + function setUp() public { + test = new ErrorsTest(); + } + + function testExpectArithmetic() public { + vm.expectRevert(stdError.arithmeticError); + test.arithmeticError(10); + } +} + +contract ErrorsTest { + function arithmeticError(uint256 a) public { + uint256 a = a - 100; + } +} +``` + +### stdStorage + +This is a rather large contract due to all of the overloading to make the UX decent. Primarily, it is a wrapper around the `record` and `accesses` cheatcodes. It can *always* find and write the storage slot(s) associated with a particular variable without knowing the storage layout. The one _major_ caveat to this is while a slot can be found for packed storage variables, we can't write to that variable safely. If a user tries to write to a packed slot, the execution throws an error, unless it is uninitialized (`bytes32(0)`). + +This works by recording all `SLOAD`s and `SSTORE`s during a function call. If there is a single slot read or written to, it immediately returns the slot. Otherwise, behind the scenes, we iterate through and check each one (assuming the user passed in a `depth` parameter). If the variable is a struct, you can pass in a `depth` parameter which is basically the field depth. + +I.e.: +```solidity +struct T { + // depth 0 + uint256 a; + // depth 1 + uint256 b; +} +``` + +#### Example usage + +```solidity +import "forge-std/Test.sol"; + +contract TestContract is Test { + using stdStorage for StdStorage; + + Storage test; + + function setUp() public { + test = new Storage(); + } + + function testFindExists() public { + // Lets say we want to find the slot for the public + // variable `exists`. We just pass in the function selector + // to the `find` command + uint256 slot = stdstore.target(address(test)).sig("exists()").find(); + assertEq(slot, 0); + } + + function testWriteExists() public { + // Lets say we want to write to the slot for the public + // variable `exists`. We just pass in the function selector + // to the `checked_write` command + stdstore.target(address(test)).sig("exists()").checked_write(100); + assertEq(test.exists(), 100); + } + + // It supports arbitrary storage layouts, like assembly based storage locations + function testFindHidden() public { + // `hidden` is a random hash of a bytes, iteration through slots would + // not find it. Our mechanism does + // Also, you can use the selector instead of a string + uint256 slot = stdstore.target(address(test)).sig(test.hidden.selector).find(); + assertEq(slot, uint256(keccak256("my.random.var"))); + } + + // If targeting a mapping, you have to pass in the keys necessary to perform the find + // i.e.: + function testFindMapping() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_addr.selector) + .with_key(address(this)) + .find(); + // in the `Storage` constructor, we wrote that this address' value was 1 in the map + // so when we load the slot, we expect it to be 1 + assertEq(uint(vm.load(address(test), bytes32(slot))), 1); + } + + // If the target is a struct, you can specify the field depth: + function testFindStruct() public { + // NOTE: see the depth parameter - 0 means 0th field, 1 means 1st field, etc. + uint256 slot_for_a_field = stdstore + .target(address(test)) + .sig(test.basicStruct.selector) + .depth(0) + .find(); + + uint256 slot_for_b_field = stdstore + .target(address(test)) + .sig(test.basicStruct.selector) + .depth(1) + .find(); + + assertEq(uint(vm.load(address(test), bytes32(slot_for_a_field))), 1); + assertEq(uint(vm.load(address(test), bytes32(slot_for_b_field))), 2); + } +} + +// A complex storage contract +contract Storage { + struct UnpackedStruct { + uint256 a; + uint256 b; + } + + constructor() { + map_addr[msg.sender] = 1; + } + + uint256 public exists = 1; + mapping(address => uint256) public map_addr; + // mapping(address => Packed) public map_packed; + mapping(address => UnpackedStruct) public map_struct; + mapping(address => mapping(address => uint256)) public deep_map; + mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; + UnpackedStruct public basicStruct = UnpackedStruct({ + a: 1, + b: 2 + }); + + function hidden() public view returns (bytes32 t) { + // an extremely hidden storage slot + bytes32 slot = keccak256("my.random.var"); + assembly { + t := sload(slot) + } + } +} +``` + +### stdCheats + +This is a wrapper over miscellaneous cheatcodes that need wrappers to be more dev friendly. Currently there are only functions related to `prank`. In general, users may expect ETH to be put into an address on `prank`, but this is not the case for safety reasons. Explicitly this `hoax` function should only be used for address that have expected balances as it will get overwritten. If an address already has ETH, you should just use `prank`. If you want to change that balance explicitly, just use `deal`. If you want to do both, `hoax` is also right for you. + + +#### Example usage: +```solidity + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +// Inherit the stdCheats +contract StdCheatsTest is Test { + Bar test; + function setUp() public { + test = new Bar(); + } + + function testHoax() public { + // we call `hoax`, which gives the target address + // eth and then calls `prank` + hoax(address(1337)); + test.bar{value: 100}(address(1337)); + + // overloaded to allow you to specify how much eth to + // initialize the address with + hoax(address(1337), 1); + test.bar{value: 1}(address(1337)); + } + + function testStartHoax() public { + // we call `startHoax`, which gives the target address + // eth and then calls `startPrank` + // + // it is also overloaded so that you can specify an eth amount + startHoax(address(1337)); + test.bar{value: 100}(address(1337)); + test.bar{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } +} + +contract Bar { + function bar(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + } +} +``` + +### Std Assertions + +Expand upon the assertion functions from the `DSTest` library. + +### `console.log` + +Usage follows the same format as [Hardhat](https://hardhat.org/hardhat-network/reference/#console-log). +It's recommended to use `console2.sol` as shown below, as this will show the decoded logs in Forge traces. + +```solidity +// import it indirectly via Test.sol +import "forge-std/Test.sol"; +// or directly import it +import "forge-std/console2.sol"; +... +console2.log(someValue); +``` + +If you need compatibility with Hardhat, you must use the standard `console.sol` instead. +Due to a bug in `console.sol`, logs that use `uint256` or `int256` types will not be properly decoded in Forge traces. + +```solidity +// import it indirectly via Test.sol +import "forge-std/Test.sol"; +// or directly import it +import "forge-std/console.sol"; +... +console.log(someValue); +``` diff --git a/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/.gitignore b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/.gitignore new file mode 100644 index 0000000..63f0b2c --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/.gitignore @@ -0,0 +1,3 @@ +/.dapple +/build +/out diff --git a/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/LICENSE b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/LICENSE @@ -0,0 +1,674 @@ + 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/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/Makefile b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/Makefile new file mode 100644 index 0000000..661dac4 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/Makefile @@ -0,0 +1,14 @@ +all:; dapp build + +test: + -dapp --use solc:0.4.23 build + -dapp --use solc:0.4.26 build + -dapp --use solc:0.5.17 build + -dapp --use solc:0.6.12 build + -dapp --use solc:0.7.5 build + +demo: + DAPP_SRC=demo dapp --use solc:0.7.5 build + -hevm dapp-test --verbose 3 + +.PHONY: test demo diff --git a/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/default.nix b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/default.nix new file mode 100644 index 0000000..cf65419 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/default.nix @@ -0,0 +1,4 @@ +{ solidityPackage, dappsys }: solidityPackage { + name = "ds-test"; + src = ./src; +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/demo/demo.sol b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/demo/demo.sol new file mode 100644 index 0000000..f3bb48e --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/demo/demo.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.5.0; + +import "../src/test.sol"; + +contract DemoTest is DSTest { + function test_this() public pure { + require(true); + } + function test_logs() public { + emit log("-- log(string)"); + emit log("a string"); + + emit log("-- log_named_uint(string, uint)"); + emit log_named_uint("uint", 512); + + emit log("-- log_named_int(string, int)"); + emit log_named_int("int", -512); + + emit log("-- log_named_address(string, address)"); + emit log_named_address("address", address(this)); + + emit log("-- log_named_bytes32(string, bytes32)"); + emit log_named_bytes32("bytes32", "a string"); + + emit log("-- log_named_bytes(string, bytes)"); + emit log_named_bytes("bytes", hex"cafefe"); + + emit log("-- log_named_string(string, string)"); + emit log_named_string("string", "a string"); + + emit log("-- log_named_decimal_uint(string, uint, uint)"); + emit log_named_decimal_uint("decimal uint", 1.0e18, 18); + + emit log("-- log_named_decimal_int(string, int, uint)"); + emit log_named_decimal_int("decimal int", -1.0e18, 18); + } + event log_old_named_uint(bytes32,uint); + function test_old_logs() public { + emit log_old_named_uint("key", 500); + emit log_named_bytes32("bkey", "val"); + } + function test_trace() public view { + this.echo("string 1", "string 2"); + } + function test_multiline() public { + emit log("a multiline\\nstring"); + emit log("a multiline string"); + emit log_bytes("a string"); + emit log_bytes("a multiline\nstring"); + emit log_bytes("a multiline\\nstring"); + emit logs(hex"0000"); + emit log_named_bytes("0x0000", hex"0000"); + emit logs(hex"ff"); + } + function echo(string memory s1, string memory s2) public pure + returns (string memory, string memory) + { + return (s1, s2); + } + + function prove_this(uint x) public { + emit log_named_uint("sym x", x); + assertGt(x + 1, 0); + } + + function test_logn() public { + assembly { + log0(0x01, 0x02) + log1(0x01, 0x02, 0x03) + log2(0x01, 0x02, 0x03, 0x04) + log3(0x01, 0x02, 0x03, 0x04, 0x05) + } + } + + event MyEvent(uint, uint indexed, uint, uint indexed); + function test_events() public { + emit MyEvent(1, 2, 3, 4); + } + + function test_asserts() public { + string memory err = "this test has failed!"; + emit log("## assertTrue(bool)\n"); + assertTrue(false); + emit log("\n"); + assertTrue(false, err); + + emit log("\n## assertEq(address,address)\n"); + assertEq(address(this), msg.sender); + emit log("\n"); + assertEq(address(this), msg.sender, err); + + emit log("\n## assertEq32(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(bytes32,bytes32)\n"); + assertEq32("bytes 1", "bytes 2"); + emit log("\n"); + assertEq32("bytes 1", "bytes 2", err); + + emit log("\n## assertEq(uint,uint)\n"); + assertEq(uint(0), 1); + emit log("\n"); + assertEq(uint(0), 1, err); + + emit log("\n## assertEq(int,int)\n"); + assertEq(-1, -2); + emit log("\n"); + assertEq(-1, -2, err); + + emit log("\n## assertEqDecimal(int,int,uint)\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertEqDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertEqDecimal(uint,uint,uint)\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGt(uint,uint)\n"); + assertGt(uint(0), 0); + emit log("\n"); + assertGt(uint(0), 0, err); + + emit log("\n## assertGt(int,int)\n"); + assertGt(-1, -1); + emit log("\n"); + assertGt(-1, -1, err); + + emit log("\n## assertGtDecimal(int,int,uint)\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGtDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGtDecimal(uint,uint,uint)\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertGe(uint,uint)\n"); + assertGe(uint(0), 1); + emit log("\n"); + assertGe(uint(0), 1, err); + + emit log("\n## assertGe(int,int)\n"); + assertGe(-1, 0); + emit log("\n"); + assertGe(-1, 0, err); + + emit log("\n## assertGeDecimal(int,int,uint)\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18); + emit log("\n"); + assertGeDecimal(-2.0e18, -1.1e18, 18, err); + + emit log("\n## assertGeDecimal(uint,uint,uint)\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18); + emit log("\n"); + assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); + + emit log("\n## assertLt(uint,uint)\n"); + assertLt(uint(0), 0); + emit log("\n"); + assertLt(uint(0), 0, err); + + emit log("\n## assertLt(int,int)\n"); + assertLt(-1, -1); + emit log("\n"); + assertLt(-1, -1, err); + + emit log("\n## assertLtDecimal(int,int,uint)\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLtDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLtDecimal(uint,uint,uint)\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertLe(uint,uint)\n"); + assertLe(uint(1), 0); + emit log("\n"); + assertLe(uint(1), 0, err); + + emit log("\n## assertLe(int,int)\n"); + assertLe(0, -1); + emit log("\n"); + assertLe(0, -1, err); + + emit log("\n## assertLeDecimal(int,int,uint)\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18); + emit log("\n"); + assertLeDecimal(-1.0e18, -1.1e18, 18, err); + + emit log("\n## assertLeDecimal(uint,uint,uint)\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18); + emit log("\n"); + assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); + + emit log("\n## assertEq(string,string)\n"); + string memory s1 = "string 1"; + string memory s2 = "string 2"; + assertEq(s1, s2); + emit log("\n"); + assertEq(s1, s2, err); + + emit log("\n## assertEq0(bytes,bytes)\n"); + assertEq0(hex"abcdef01", hex"abcdef02"); + emit log("\n"); + assertEq0(hex"abcdef01", hex"abcdef02", err); + } +} + +contract DemoTestWithSetUp { + function setUp() public { + } + function test_pass() public pure { + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/test.sol b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/test.sol new file mode 100644 index 0000000..515a3bd --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/test.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// 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 . + +pragma solidity >=0.5.0; + +contract DSTest { + event log (string); + event logs (bytes); + + event log_address (address); + event log_bytes32 (bytes32); + event log_int (int); + event log_uint (uint); + event log_bytes (bytes); + event log_string (string); + + event log_named_address (string key, address val); + event log_named_bytes32 (string key, bytes32 val); + event log_named_decimal_int (string key, int val, uint decimals); + event log_named_decimal_uint (string key, uint val, uint decimals); + event log_named_int (string key, int val); + event log_named_uint (string key, uint val); + event log_named_bytes (string key, bytes val); + event log_named_string (string key, string val); + + bool public IS_TEST = true; + bool private _failed; + + address constant HEVM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + modifier mayRevert() { _; } + modifier testopts(string memory) { _; } + + function failed() public returns (bool) { + if (_failed) { + return _failed; + } else { + bool globalFailed = false; + if (hasHEVMContext()) { + (, bytes memory retdata) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("load(address,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed")) + ) + ); + globalFailed = abi.decode(retdata, (bool)); + } + return globalFailed; + } + } + + function fail() internal { + if (hasHEVMContext()) { + (bool status, ) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("store(address,bytes32,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) + ) + ); + status; // Silence compiler warnings + } + _failed = true; + } + + function hasHEVMContext() internal view returns (bool) { + uint256 hevmCodeSize = 0; + assembly { + hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) + } + return hevmCodeSize > 0; + } + + modifier logs_gas() { + uint startGas = gasleft(); + _; + uint endGas = gasleft(); + emit log_named_uint("gas", startGas - endGas); + } + + function assertTrue(bool condition) internal { + if (!condition) { + emit log("Error: Assertion Failed"); + fail(); + } + } + + function assertTrue(bool condition, string memory err) internal { + if (!condition) { + emit log_named_string("Error", err); + assertTrue(condition); + } + } + + function assertEq(address a, address b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [address]"); + emit log_named_address(" Expected", b); + emit log_named_address(" Actual", a); + fail(); + } + } + function assertEq(address a, address b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes32 a, bytes32 b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [bytes32]"); + emit log_named_bytes32(" Expected", b); + emit log_named_bytes32(" Actual", a); + fail(); + } + } + function assertEq(bytes32 a, bytes32 b, string memory err) internal { + if (a != b) { + emit log_named_string ("Error", err); + assertEq(a, b); + } + } + function assertEq32(bytes32 a, bytes32 b) internal { + assertEq(a, b); + } + function assertEq32(bytes32 a, bytes32 b, string memory err) internal { + assertEq(a, b, err); + } + + function assertEq(int a, int b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [int]"); + emit log_named_int(" Expected", b); + emit log_named_int(" Actual", a); + fail(); + } + } + function assertEq(int a, int b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEq(uint a, uint b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [uint]"); + emit log_named_uint(" Expected", b); + emit log_named_uint(" Actual", a); + fail(); + } + } + function assertEq(uint a, uint b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEqDecimal(int a, int b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal int]"); + emit log_named_decimal_int(" Expected", b, decimals); + emit log_named_decimal_int(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + function assertEqDecimal(uint a, uint b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Expected", b, decimals); + emit log_named_decimal_uint(" Actual", a, decimals); + fail(); + } + } + function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + + function assertGt(uint a, uint b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGt(uint a, uint b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGt(int a, int b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGt(int a, int b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGtDecimal(int a, int b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + function assertGtDecimal(uint a, uint b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + + function assertGe(uint a, uint b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGe(uint a, uint b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGe(int a, int b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGe(int a, int b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGeDecimal(int a, int b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + function assertGeDecimal(uint a, uint b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertLt(uint a, uint b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLt(uint a, uint b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLt(int a, int b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLt(int a, int b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLtDecimal(int a, int b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + function assertLtDecimal(uint a, uint b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + + function assertLe(uint a, uint b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLe(uint a, uint b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLe(int a, int b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLe(int a, int b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLeDecimal(int a, int b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } + } + function assertLeDecimal(uint a, uint b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log("Error: a == b not satisfied [string]"); + emit log_named_string(" Expected", b); + emit log_named_string(" Actual", a); + fail(); + } + } + function assertEq(string memory a, string memory b, string memory err) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { + ok = true; + if (a.length == b.length) { + for (uint i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + ok = false; + } + } + } else { + ok = false; + } + } + function assertEq0(bytes memory a, bytes memory b) internal { + if (!checkEq0(a, b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Expected", b); + emit log_named_bytes(" Actual", a); + fail(); + } + } + function assertEq0(bytes memory a, bytes memory b, string memory err) internal { + if (!checkEq0(a, b)) { + emit log_named_string("Error", err); + assertEq0(a, b); + } + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/Script.sol b/lib/openzeppelin-contracts/lib/forge-std/src/Script.sol new file mode 100644 index 0000000..f4b7efa --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/Script.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; + +import "./console.sol"; +import "./console2.sol"; +import "./StdJson.sol"; + +abstract contract Script { + bool public IS_SCRIPT = true; + address constant private VM_ADDRESS = + address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); + + Vm public constant vm = Vm(VM_ADDRESS); + + /// @dev Compute the address a contract will be deployed at for a given deployer address and nonce + /// @notice adapated from Solmate implementation (https://github.com/transmissions11/solmate/blob/main/src/utils/LibRLP.sol) + function computeCreateAddress(address deployer, uint256 nonce) internal pure returns (address) { + // The integer zero is treated as an empty byte string, and as a result it only has a length prefix, 0x80, computed via 0x80 + 0. + // A one byte integer uses its own value as its length prefix, there is no additional "0x80 + length" prefix that comes before it. + if (nonce == 0x00) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80)))); + if (nonce <= 0x7f) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce)))); + + // Nonces greater than 1 byte all follow a consistent encoding scheme, where each value is preceded by a prefix of 0x80 + length. + if (nonce <= 2**8 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce)))); + if (nonce <= 2**16 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce)))); + if (nonce <= 2**24 - 1) return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce)))); + + // More details about RLP encoding can be found here: https://eth.wiki/fundamentals/rlp + // 0xda = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x84 ++ nonce) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + // 0x84 = 0x80 + 0x04 (0x04 = the bytes length of the nonce, 4 bytes, in hex) + // We assume nobody can have a nonce large enough to require more than 32 bytes. + return addressFromLast20Bytes(keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce)))); + } + + function addressFromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { + return address(uint160(uint256(bytesValue))); + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/StdJson.sol b/lib/openzeppelin-contracts/lib/forge-std/src/StdJson.sol new file mode 100644 index 0000000..c4ad825 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/StdJson.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./Vm.sol"; + +// Helpers for parsing keys into types. +library stdJson { + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function parseRaw(string memory json, string memory key) + internal + returns (bytes memory) + { + return vm.parseJson(json, key); + } + + function readUint(string memory json, string memory key) + internal + returns (uint256) + { + return abi.decode(vm.parseJson(json, key), (uint256)); + } + + function readUintArray(string memory json, string memory key) + internal + returns (uint256[] memory) + { + return abi.decode(vm.parseJson(json, key), (uint256[])); + } + + function readInt(string memory json, string memory key) + internal + returns (int256) + { + return abi.decode(vm.parseJson(json, key), (int256)); + } + + function readIntArray(string memory json, string memory key) + internal + returns (int256[] memory) + { + return abi.decode(vm.parseJson(json, key), (int256[])); + } + + function readBytes32(string memory json, string memory key) + internal + returns (bytes32) + { + return abi.decode(vm.parseJson(json, key), (bytes32)); + } + + function readBytes32Array(string memory json, string memory key) + internal + returns (bytes32[] memory) + { + return abi.decode(vm.parseJson(json, key), (bytes32[])); + } + + function readString(string memory json, string memory key) + internal + returns (string memory) + { + return abi.decode(vm.parseJson(json, key), (string)); + } + + function readStringArray(string memory json, string memory key) + internal + returns (string[] memory) + { + return abi.decode(vm.parseJson(json, key), (string[])); + } + + function readAddress(string memory json, string memory key) + internal + returns (address) + { + return abi.decode(vm.parseJson(json, key), (address)); + } + + function readAddressArray(string memory json, string memory key) + internal + returns (address[] memory) + { + return abi.decode(vm.parseJson(json, key), (address[])); + } + + function readBool(string memory json, string memory key) + internal + returns (bool) + { + return abi.decode(vm.parseJson(json, key), (bool)); + } + + function readBoolArray(string memory json, string memory key) + internal + returns (bool[] memory) + { + return abi.decode(vm.parseJson(json, key), (bool[])); + } + + function readBytes(string memory json, string memory key) + internal + returns (bytes memory) + { + return abi.decode(vm.parseJson(json, key), (bytes)); + } + + function readBytesArray(string memory json, string memory key) + internal + returns (bytes[] memory) + { + return abi.decode(vm.parseJson(json, key), (bytes[])); + } + + +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/Test.sol b/lib/openzeppelin-contracts/lib/forge-std/src/Test.sol new file mode 100644 index 0000000..2fb0bc9 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/Test.sol @@ -0,0 +1,1134 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "./Script.sol"; +import "ds-test/test.sol"; + +// Wrappers around Cheatcodes to avoid footguns +abstract contract Test is DSTest, Script { + using stdStorage for StdStorage; + + uint256 internal constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; + + StdStorage internal stdstore; + + /*////////////////////////////////////////////////////////////////////////// + STD-LOGS + //////////////////////////////////////////////////////////////////////////*/ + + event log_array(uint256[] val); + event log_array(int256[] val); + event log_array(address[] val); + event log_named_array(string key, uint256[] val); + event log_named_array(string key, int256[] val); + event log_named_array(string key, address[] val); + + /*////////////////////////////////////////////////////////////////////////// + STD-CHEATS + //////////////////////////////////////////////////////////////////////////*/ + + // Skip forward or rewind time by the specified number of seconds + function skip(uint256 time) internal { + vm.warp(block.timestamp + time); + } + + function rewind(uint256 time) internal { + vm.warp(block.timestamp - time); + } + + // Setup a prank from an address that has some ether + function hoax(address who) internal { + vm.deal(who, 1 << 128); + vm.prank(who); + } + + function hoax(address who, uint256 give) internal { + vm.deal(who, give); + vm.prank(who); + } + + function hoax(address who, address origin) internal { + vm.deal(who, 1 << 128); + vm.prank(who, origin); + } + + function hoax(address who, address origin, uint256 give) internal { + vm.deal(who, give); + vm.prank(who, origin); + } + + // Start perpetual prank from an address that has some ether + function startHoax(address who) internal { + vm.deal(who, 1 << 128); + vm.startPrank(who); + } + + function startHoax(address who, uint256 give) internal { + vm.deal(who, give); + vm.startPrank(who); + } + + // Start perpetual prank from an address that has some ether + // tx.origin is set to the origin parameter + function startHoax(address who, address origin) internal { + vm.deal(who, 1 << 128); + vm.startPrank(who, origin); + } + + function startHoax(address who, address origin, uint256 give) internal { + vm.deal(who, give); + vm.startPrank(who, origin); + } + + function changePrank(address who) internal { + vm.stopPrank(); + vm.startPrank(who); + } + + // creates a labeled address and the corresponding private key + function makeAddrAndKey(string memory name) internal returns(address addr, uint256 privateKey) { + privateKey = uint256(keccak256(abi.encodePacked(name))); + addr = vm.addr(privateKey); + vm.label(addr, name); + } + + // creates a labeled address + function makeAddr(string memory name) internal returns(address addr) { + (addr,) = makeAddrAndKey(name); + } + + // DEPRECATED: Use `deal` instead + function tip(address token, address to, uint256 give) internal { + emit log_named_string("WARNING", "Test tip(address,address,uint256): The `tip` stdcheat has been deprecated. Use `deal` instead."); + stdstore + .target(token) + .sig(0x70a08231) + .with_key(to) + .checked_write(give); + } + + // The same as Vm's `deal` + // Use the alternative signature for ERC20 tokens + function deal(address to, uint256 give) internal { + vm.deal(to, give); + } + + // Set the balance of an account for any ERC20 token + // Use the alternative signature to update `totalSupply` + function deal(address token, address to, uint256 give) internal { + deal(token, to, give, false); + } + + function deal(address token, address to, uint256 give, bool adjust) internal { + // get current balance + (, bytes memory balData) = token.call(abi.encodeWithSelector(0x70a08231, to)); + uint256 prevBal = abi.decode(balData, (uint256)); + + // update balance + stdstore + .target(token) + .sig(0x70a08231) + .with_key(to) + .checked_write(give); + + // update total supply + if(adjust){ + (, bytes memory totSupData) = token.call(abi.encodeWithSelector(0x18160ddd)); + uint256 totSup = abi.decode(totSupData, (uint256)); + if(give < prevBal) { + totSup -= (prevBal - give); + } else { + totSup += (give - prevBal); + } + stdstore + .target(token) + .sig(0x18160ddd) + .checked_write(totSup); + } + } + + function bound(uint256 x, uint256 min, uint256 max) internal virtual returns (uint256 result) { + require(min <= max, "Test bound(uint256,uint256,uint256): Max is less than min."); + + uint256 size = max - min; + + if (size == 0) + { + result = min; + } + else if (size == UINT256_MAX) + { + result = x; + } + else + { + ++size; // make `max` inclusive + uint256 mod = x % size; + result = min + mod; + } + + emit log_named_uint("Bound Result", result); + } + + // Deploy a contract by fetching the contract bytecode from + // the artifacts directory + // e.g. `deployCode(code, abi.encode(arg1,arg2,arg3))` + function deployCode(string memory what, bytes memory args) + internal + returns (address addr) + { + bytes memory bytecode = abi.encodePacked(vm.getCode(what), args); + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string,bytes): Deployment failed." + ); + } + + function deployCode(string memory what) + internal + returns (address addr) + { + bytes memory bytecode = vm.getCode(what); + /// @solidity memory-safe-assembly + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string): Deployment failed." + ); + } + + /// deploy contract with value on construction + function deployCode(string memory what, bytes memory args, uint256 val) + internal + returns (address addr) + { + bytes memory bytecode = abi.encodePacked(vm.getCode(what), args); + /// @solidity memory-safe-assembly + assembly { + addr := create(val, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string,bytes,uint256): Deployment failed." + ); + } + + function deployCode(string memory what, uint256 val) + internal + returns (address addr) + { + bytes memory bytecode = vm.getCode(what); + /// @solidity memory-safe-assembly + assembly { + addr := create(val, add(bytecode, 0x20), mload(bytecode)) + } + + require( + addr != address(0), + "Test deployCode(string,uint256): Deployment failed." + ); + } + + /*////////////////////////////////////////////////////////////////////////// + STD-ASSERTIONS + //////////////////////////////////////////////////////////////////////////*/ + + function fail(string memory err) internal virtual { + emit log_named_string("Error", err); + fail(); + } + + function assertFalse(bool data) internal virtual { + assertTrue(!data); + } + + function assertFalse(bool data, string memory err) internal virtual { + assertTrue(!data, err); + } + + function assertEq(bool a, bool b) internal { + if (a != b) { + emit log ("Error: a == b not satisfied [bool]"); + emit log_named_string (" Expected", b ? "true" : "false"); + emit log_named_string (" Actual", a ? "true" : "false"); + fail(); + } + } + + function assertEq(bool a, bool b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(bytes memory a, bytes memory b) internal { + assertEq0(a, b); + } + + function assertEq(bytes memory a, bytes memory b, string memory err) internal { + assertEq0(a, b, err); + } + + function assertEq(uint256[] memory a, uint256[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [uint[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(int256[] memory a, int256[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [int[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(address[] memory a, address[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [address[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function assertEq(uint256[] memory a, uint256[] memory b, string memory err) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertEq(int256[] memory a, int256[] memory b, string memory err) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + + function assertEq(address[] memory a, address[] memory b, string memory err) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + + function assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log ("Error: a ~= b not satisfied [uint]"); + emit log_named_uint (" Expected", b); + emit log_named_uint (" Actual", a); + emit log_named_uint (" Max Delta", maxDelta); + emit log_named_uint (" Delta", delta); + fail(); + } + } + + function assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta, + string memory err + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log_named_string ("Error", err); + assertApproxEqAbs(a, b, maxDelta); + } + } + + function assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log ("Error: a ~= b not satisfied [int]"); + emit log_named_int (" Expected", b); + emit log_named_int (" Actual", a); + emit log_named_uint (" Max Delta", maxDelta); + emit log_named_uint (" Delta", delta); + fail(); + } + } + + function assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta, + string memory err + ) internal virtual { + uint256 delta = stdMath.delta(a, b); + + if (delta > maxDelta) { + emit log_named_string ("Error", err); + assertApproxEqAbs(a, b, maxDelta); + } + } + + function assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log ("Error: a ~= b not satisfied [uint]"); + emit log_named_uint (" Expected", b); + emit log_named_uint (" Actual", a); + emit log_named_decimal_uint (" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint (" % Delta", percentDelta, 18); + fail(); + } + } + + function assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100% + string memory err + ) internal virtual { + if (b == 0) return assertEq(a, b, err); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log_named_string ("Error", err); + assertApproxEqRel(a, b, maxPercentDelta); + } + } + + function assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log ("Error: a ~= b not satisfied [int]"); + emit log_named_int (" Expected", b); + emit log_named_int (" Actual", a); + emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); + emit log_named_decimal_uint(" % Delta", percentDelta, 18); + fail(); + } + } + + function assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta, + string memory err + ) internal virtual { + if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. + + uint256 percentDelta = stdMath.percentDelta(a, b); + + if (percentDelta > maxPercentDelta) { + emit log_named_string ("Error", err); + assertApproxEqRel(a, b, maxPercentDelta); + } + } + + /*////////////////////////////////////////////////////////////// + JSON PARSING + //////////////////////////////////////////////////////////////*/ + + // Data structures to parse Transaction objects from the broadcast artifact + // that conform to EIP1559. The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct RawTx1559 { + string[] arguments; + address contractAddress; + string contractName; + // json value name = function + string functionSig; + bytes32 hash; + // json value name = tx + RawTx1559Detail txDetail; + // json value name = type + string opcode; + } + + struct RawTx1559Detail { + AccessList[] accessList; + bytes data; + address from; + bytes gas; + bytes nonce; + address to; + bytes txType; + bytes value; + } + + struct Tx1559 { + string[] arguments; + address contractAddress; + string contractName; + string functionSig; + bytes32 hash; + Tx1559Detail txDetail; + string opcode; + } + + struct Tx1559Detail { + AccessList[] accessList; + bytes data; + address from; + uint256 gas; + uint256 nonce; + address to; + uint256 txType; + uint256 value; + } + + // Data structures to parse Transaction objects from the broadcast artifact + // that DO NOT conform to EIP1559. The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct TxLegacy{ + string[] arguments; + address contractAddress; + string contractName; + string functionSig; + string hash; + string opcode; + TxDetailLegacy transaction; + } + + struct TxDetailLegacy{ + AccessList[] accessList; + uint256 chainId; + bytes data; + address from; + uint256 gas; + uint256 gasPrice; + bytes32 hash; + uint256 nonce; + bytes1 opcode; + bytes32 r; + bytes32 s; + uint256 txType; + address to; + uint8 v; + uint256 value; + } + + struct AccessList{ + address accessAddress; + bytes32[] storageKeys; + } + + // Data structures to parse Receipt objects from the broadcast artifact. + // The Raw structs is what is parsed from the JSON + // and then converted to the one that is used by the user for better UX. + + struct RawReceipt { + bytes32 blockHash; + bytes blockNumber; + address contractAddress; + bytes cumulativeGasUsed; + bytes effectiveGasPrice; + address from; + bytes gasUsed; + RawReceiptLog[] logs; + bytes logsBloom; + bytes status; + address to; + bytes32 transactionHash; + bytes transactionIndex; + } + + struct Receipt { + bytes32 blockHash; + uint256 blockNumber; + address contractAddress; + uint256 cumulativeGasUsed; + uint256 effectiveGasPrice; + address from; + uint256 gasUsed; + ReceiptLog[] logs; + bytes logsBloom; + uint256 status; + address to; + bytes32 transactionHash; + uint256 transactionIndex; + } + + // Data structures to parse the entire broadcast artifact, assuming the + // transactions conform to EIP1559. + + struct EIP1559ScriptArtifact { + string[] libraries; + string path; + string[] pending; + Receipt[] receipts; + uint256 timestamp; + Tx1559[] transactions; + TxReturn[] txReturns; + } + + struct RawEIP1559ScriptArtifact { + string[] libraries; + string path; + string[] pending; + RawReceipt[] receipts; + TxReturn[] txReturns; + uint256 timestamp; + RawTx1559[] transactions; + } + + struct RawReceiptLog { + // json value = address + address logAddress; + bytes32 blockHash; + bytes blockNumber; + bytes data; + bytes logIndex; + bool removed; + bytes32[] topics; + bytes32 transactionHash; + bytes transactionIndex; + bytes transactionLogIndex; + } + + struct ReceiptLog { + // json value = address + address logAddress; + bytes32 blockHash; + uint256 blockNumber; + bytes data; + uint256 logIndex; + bytes32[] topics; + uint256 transactionIndex; + uint256 transactionLogIndex; + bool removed; + } + + struct TxReturn { + string internalType; + string value; + } + + + function readEIP1559ScriptArtifact(string memory path) + internal + returns(EIP1559ScriptArtifact memory) + { + string memory data = vm.readFile(path); + bytes memory parsedData = vm.parseJson(data); + RawEIP1559ScriptArtifact memory rawArtifact = abi.decode(parsedData, (RawEIP1559ScriptArtifact)); + EIP1559ScriptArtifact memory artifact; + artifact.libraries = rawArtifact.libraries; + artifact.path = rawArtifact.path; + artifact.timestamp = rawArtifact.timestamp; + artifact.pending = rawArtifact.pending; + artifact.txReturns = rawArtifact.txReturns; + artifact.receipts = rawToConvertedReceipts(rawArtifact.receipts); + artifact.transactions = rawToConvertedEIPTx1559s(rawArtifact.transactions); + return artifact; + } + + function rawToConvertedEIPTx1559s(RawTx1559[] memory rawTxs) + internal pure + returns (Tx1559[] memory) + { + Tx1559[] memory txs = new Tx1559[](rawTxs.length); + for (uint i; i < rawTxs.length; i++) { + txs[i] = rawToConvertedEIPTx1559(rawTxs[i]); + } + return txs; + } + + function rawToConvertedEIPTx1559(RawTx1559 memory rawTx) + internal pure + returns (Tx1559 memory) + { + Tx1559 memory transaction; + transaction.arguments = rawTx.arguments; + transaction.contractName = rawTx.contractName; + transaction.functionSig = rawTx.functionSig; + transaction.hash= rawTx.hash; + transaction.txDetail = rawToConvertedEIP1559Detail(rawTx.txDetail); + transaction.opcode= rawTx.opcode; + return transaction; + } + + function rawToConvertedEIP1559Detail(RawTx1559Detail memory rawDetail) + internal pure + returns (Tx1559Detail memory) + { + Tx1559Detail memory txDetail; + txDetail.data = rawDetail.data; + txDetail.from = rawDetail.from; + txDetail.to = rawDetail.to; + txDetail.nonce = bytesToUint(rawDetail.nonce); + txDetail.txType = bytesToUint(rawDetail.txType); + txDetail.value = bytesToUint(rawDetail.value); + txDetail.gas = bytesToUint(rawDetail.gas); + txDetail.accessList = rawDetail.accessList; + return txDetail; + + } + + function readTx1559s(string memory path) + internal + returns (Tx1559[] memory) + { + string memory deployData = vm.readFile(path); + bytes memory parsedDeployData = + vm.parseJson(deployData, ".transactions"); + RawTx1559[] memory rawTxs = abi.decode(parsedDeployData, (RawTx1559[])); + return rawToConvertedEIPTx1559s(rawTxs); + } + + + function readTx1559(string memory path, uint256 index) + internal + returns (Tx1559 memory) + { + string memory deployData = vm.readFile(path); + string memory key = string(abi.encodePacked(".transactions[",vm.toString(index), "]")); + bytes memory parsedDeployData = + vm.parseJson(deployData, key); + RawTx1559 memory rawTx = abi.decode(parsedDeployData, (RawTx1559)); + return rawToConvertedEIPTx1559(rawTx); + } + + + // Analogous to readTransactions, but for receipts. + function readReceipts(string memory path) + internal + returns (Receipt[] memory) + { + string memory deployData = vm.readFile(path); + bytes memory parsedDeployData = vm.parseJson(deployData, ".receipts"); + RawReceipt[] memory rawReceipts = abi.decode(parsedDeployData, (RawReceipt[])); + return rawToConvertedReceipts(rawReceipts); + } + + function readReceipt(string memory path, uint index) + internal + returns (Receipt memory) + { + string memory deployData = vm.readFile(path); + string memory key = string(abi.encodePacked(".receipts[",vm.toString(index), "]")); + bytes memory parsedDeployData = vm.parseJson(deployData, key); + RawReceipt memory rawReceipt = abi.decode(parsedDeployData, (RawReceipt)); + return rawToConvertedReceipt(rawReceipt); + } + + function rawToConvertedReceipts(RawReceipt[] memory rawReceipts) + internal pure + returns(Receipt[] memory) + { + Receipt[] memory receipts = new Receipt[](rawReceipts.length); + for (uint i; i < rawReceipts.length; i++) { + receipts[i] = rawToConvertedReceipt(rawReceipts[i]); + } + return receipts; + } + + function rawToConvertedReceipt(RawReceipt memory rawReceipt) + internal pure + returns(Receipt memory) + { + Receipt memory receipt; + receipt.blockHash = rawReceipt.blockHash; + receipt.to = rawReceipt.to; + receipt.from = rawReceipt.from; + receipt.contractAddress = rawReceipt.contractAddress; + receipt.effectiveGasPrice = bytesToUint(rawReceipt.effectiveGasPrice); + receipt.cumulativeGasUsed= bytesToUint(rawReceipt.cumulativeGasUsed); + receipt.gasUsed = bytesToUint(rawReceipt.gasUsed); + receipt.status = bytesToUint(rawReceipt.status); + receipt.transactionIndex = bytesToUint(rawReceipt.transactionIndex); + receipt.blockNumber = bytesToUint(rawReceipt.blockNumber); + receipt.logs = rawToConvertedReceiptLogs(rawReceipt.logs); + receipt.logsBloom = rawReceipt.logsBloom; + receipt.transactionHash = rawReceipt.transactionHash; + return receipt; + } + + function rawToConvertedReceiptLogs(RawReceiptLog[] memory rawLogs) + internal pure + returns (ReceiptLog[] memory) + { + ReceiptLog[] memory logs = new ReceiptLog[](rawLogs.length); + for (uint i; i < rawLogs.length; i++) { + logs[i].logAddress = rawLogs[i].logAddress; + logs[i].blockHash = rawLogs[i].blockHash; + logs[i].blockNumber = bytesToUint(rawLogs[i].blockNumber); + logs[i].data = rawLogs[i].data; + logs[i].logIndex = bytesToUint(rawLogs[i].logIndex); + logs[i].topics = rawLogs[i].topics; + logs[i].transactionIndex = bytesToUint(rawLogs[i].transactionIndex); + logs[i].transactionLogIndex = bytesToUint(rawLogs[i].transactionLogIndex); + logs[i].removed = rawLogs[i].removed; + } + return logs; + + } + + function bytesToUint(bytes memory b) internal pure returns (uint256){ + uint256 number; + for (uint i=0; i < b.length; i++) { + number = number + uint(uint8(b[i]))*(2**(8*(b.length-(i+1)))); + } + return number; + } + +} + +/*////////////////////////////////////////////////////////////////////////// + STD-ERRORS +//////////////////////////////////////////////////////////////////////////*/ + +library stdError { + bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); + bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); + bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); + bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); + bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); + bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); + bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); + bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); + bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); + // DEPRECATED: Use Vm's `expectRevert` without any arguments instead + bytes public constant lowLevelError = bytes(""); // `0x` +} + +/*////////////////////////////////////////////////////////////////////////// + STD-STORAGE +//////////////////////////////////////////////////////////////////////////*/ + +struct StdStorage { + mapping (address => mapping(bytes4 => mapping(bytes32 => uint256))) slots; + mapping (address => mapping(bytes4 => mapping(bytes32 => bool))) finds; + + bytes32[] _keys; + bytes4 _sig; + uint256 _depth; + address _target; + bytes32 _set; +} + +library stdStorage { + event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint slot); + event WARNING_UninitedSlot(address who, uint slot); + + uint256 private constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935; + int256 private constant INT256_MAX = 57896044618658097711785492504343953926634992332820282019728792003956564819967; + + Vm private constant vm_std_store = Vm(address(uint160(uint256(keccak256('hevm cheat code'))))); + + function sigs( + string memory sigStr + ) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(bytes(sigStr))); + } + + /// @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( + StdStorage storage self + ) + internal + returns (uint256) + { + address who = self._target; + bytes4 fsig = self._sig; + uint256 field_depth = self._depth; + bytes32[] memory ins = self._keys; + + // calldata to test against + if (self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { + return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; + } + bytes memory cald = abi.encodePacked(fsig, flatten(ins)); + vm_std_store.record(); + bytes32 fdat; + { + (, bytes memory rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32*field_depth); + } + + (bytes32[] memory reads, ) = vm_std_store.accesses(address(who)); + if (reads.length == 1) { + bytes32 curr = vm_std_store.load(who, reads[0]); + if (curr == bytes32(0)) { + emit WARNING_UninitedSlot(who, uint256(reads[0])); + } + if (fdat != curr) { + require(false, "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported."); + } + emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[0])); + self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[0]); + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; + } else if (reads.length > 1) { + for (uint256 i = 0; i < reads.length; i++) { + bytes32 prev = vm_std_store.load(who, reads[i]); + if (prev == bytes32(0)) { + emit WARNING_UninitedSlot(who, uint256(reads[i])); + } + // store + vm_std_store.store(who, reads[i], bytes32(hex"1337")); + bool success; + bytes memory rdat; + { + (success, rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32*field_depth); + } + + if (success && fdat == bytes32(hex"1337")) { + // we found which of the slots is the actual one + emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[i])); + self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[i]); + self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; + vm_std_store.store(who, reads[i], prev); + break; + } + vm_std_store.store(who, reads[i], prev); + } + } else { + require(false, "stdStorage find(StdStorage): No storage use detected for target."); + } + + require(self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))], "stdStorage find(StdStorage): Slot(s) not found."); + + delete self._target; + delete self._sig; + delete self._keys; + delete self._depth; + + return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; + } + + function target(StdStorage storage self, address _target) internal returns (StdStorage storage) { + self._target = _target; + return self; + } + + function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) { + self._sig = _sig; + return self; + } + + function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) { + self._sig = sigs(_sig); + return self; + } + + function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) { + self._keys.push(bytes32(uint256(uint160(who)))); + return self; + } + + function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) { + self._keys.push(bytes32(amt)); + return self; + } + function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) { + self._keys.push(key); + return self; + } + + function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) { + self._depth = _depth; + return self; + } + + function checked_write(StdStorage storage self, address who) internal { + checked_write(self, bytes32(uint256(uint160(who)))); + } + + function checked_write(StdStorage storage self, uint256 amt) internal { + checked_write(self, bytes32(amt)); + } + + function checked_write(StdStorage storage self, bool write) internal { + bytes32 t; + /// @solidity memory-safe-assembly + assembly { + t := write + } + checked_write(self, t); + } + + function checked_write( + StdStorage storage self, + bytes32 set + ) internal { + address who = self._target; + bytes4 fsig = self._sig; + uint256 field_depth = self._depth; + bytes32[] memory ins = self._keys; + + bytes memory cald = abi.encodePacked(fsig, flatten(ins)); + if (!self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { + find(self); + } + bytes32 slot = bytes32(self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]); + + bytes32 fdat; + { + (, bytes memory rdat) = who.staticcall(cald); + fdat = bytesToBytes32(rdat, 32*field_depth); + } + bytes32 curr = vm_std_store.load(who, slot); + + if (fdat != curr) { + require(false, "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported."); + } + vm_std_store.store(who, slot, set); + delete self._target; + delete self._sig; + delete self._keys; + delete self._depth; + } + + function read(StdStorage storage self) private returns (bytes memory) { + address t = self._target; + uint256 s = find(self); + return abi.encode(vm_std_store.load(t, bytes32(s))); + } + + function read_bytes32(StdStorage storage self) internal returns (bytes32) { + return abi.decode(read(self), (bytes32)); + } + + + function read_bool(StdStorage storage self) internal returns (bool) { + int256 v = read_int(self); + if (v == 0) return false; + if (v == 1) return true; + revert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); + } + + function read_address(StdStorage storage self) internal returns (address) { + return abi.decode(read(self), (address)); + } + + function read_uint(StdStorage storage self) internal returns (uint256) { + return abi.decode(read(self), (uint256)); + } + + function read_int(StdStorage storage self) internal returns (int256) { + return abi.decode(read(self), (int256)); + } + + function bytesToBytes32(bytes memory b, uint offset) public pure returns (bytes32) { + bytes32 out; + + uint256 max = b.length > 32 ? 32 : b.length; + for (uint i = 0; i < max; i++) { + out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); + } + return out; + } + + function flatten(bytes32[] memory b) private pure returns (bytes memory) + { + bytes memory result = new bytes(b.length * 32); + for (uint256 i = 0; i < b.length; i++) { + bytes32 k = b[i]; + /// @solidity memory-safe-assembly + assembly { + mstore(add(result, add(32, mul(32, i))), k) + } + } + + return result; + } + + + +} + + +/*////////////////////////////////////////////////////////////////////////// + STD-MATH +//////////////////////////////////////////////////////////////////////////*/ + +library stdMath { + int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968; + + function abs(int256 a) internal pure returns (uint256) { + // Required or it will fail when `a = type(int256).min` + if (a == INT256_MIN) + return 57896044618658097711785492504343953926634992332820282019728792003956564819968; + + return uint256(a > 0 ? a : -a); + } + + function delta(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b + ? a - b + : b - a; + } + + function delta(int256 a, int256 b) internal pure returns (uint256) { + // a and b are of the same sign + // this works thanks to two's complement, the left-most bit is the sign bit + if ((a ^ b) > -1) { + return delta(abs(a), abs(b)); + } + + // a and b are of opposite signs + return abs(a) + abs(b); + } + + function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 absDelta = delta(a, b); + + return absDelta * 1e18 / b; + } + + function percentDelta(int256 a, int256 b) internal pure returns (uint256) { + uint256 absDelta = delta(a, b); + uint256 absB = abs(b); + + return absDelta * 1e18 / absB; + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/Vm.sol b/lib/openzeppelin-contracts/lib/forge-std/src/Vm.sol new file mode 100644 index 0000000..f204cf0 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/Vm.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; +pragma experimental ABIEncoderV2; + +interface Vm { + struct Log { + bytes32[] topics; + bytes data; + } + + // Sets block.timestamp (newTimestamp) + function warp(uint256) external; + // Sets block.height (newHeight) + function roll(uint256) external; + // Sets block.basefee (newBasefee) + function fee(uint256) external; + // Sets block.difficulty (newDifficulty) + function difficulty(uint256) external; + // Sets block.chainid + function chainId(uint256) external; + // Loads a storage slot from an address (who, slot) + function load(address,bytes32) external returns (bytes32); + // Stores a value to an address' storage slot, (who, slot, value) + function store(address,bytes32,bytes32) external; + // Signs data, (privateKey, digest) => (v, r, s) + function sign(uint256,bytes32) external returns (uint8,bytes32,bytes32); + // Gets the address for a given private key, (privateKey) => (address) + function addr(uint256) external returns (address); + // Gets the nonce of an account + function getNonce(address) external returns (uint64); + // Sets the nonce of an account; must be higher than the current nonce of the account + function setNonce(address, uint64) external; + // Performs a foreign function call via the terminal, (stringInputs) => (result) + function ffi(string[] calldata) external returns (bytes memory); + // Sets environment variables, (name, value) + function setEnv(string calldata, string calldata) external; + // Reads environment variables, (name) => (value) + function envBool(string calldata) external returns (bool); + function envUint(string calldata) external returns (uint256); + function envInt(string calldata) external returns (int256); + function envAddress(string calldata) external returns (address); + function envBytes32(string calldata) external returns (bytes32); + function envString(string calldata) external returns (string memory); + function envBytes(string calldata) external returns (bytes memory); + // Reads environment variables as arrays, (name, delim) => (value[]) + function envBool(string calldata, string calldata) external returns (bool[] memory); + function envUint(string calldata, string calldata) external returns (uint256[] memory); + function envInt(string calldata, string calldata) external returns (int256[] memory); + function envAddress(string calldata, string calldata) external returns (address[] memory); + function envBytes32(string calldata, string calldata) external returns (bytes32[] memory); + function envString(string calldata, string calldata) external returns (string[] memory); + function envBytes(string calldata, string calldata) external returns (bytes[] memory); + // Sets the *next* call's msg.sender to be the input address + function prank(address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called + function startPrank(address) external; + // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input + function prank(address,address) external; + // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input + function startPrank(address,address) external; + // Resets subsequent calls' msg.sender to be `address(this)` + function stopPrank() external; + // Sets an address' balance, (who, newBalance) + function deal(address, uint256) external; + // Sets an address' code, (who, newCode) + function etch(address, bytes calldata) external; + // Expects an error on next call + function expectRevert(bytes calldata) external; + function expectRevert(bytes4) external; + function expectRevert() external; + // Records all storage reads and writes + function record() external; + // Gets all accessed reads and write slot from a recording session, for a given address + function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes); + // Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). + // Call this function, then emit an event, then call a function. Internally after the call, we check if + // logs were emitted in the expected order with the expected topics and data (as specified by the booleans) + function expectEmit(bool,bool,bool,bool) external; + function expectEmit(bool,bool,bool,bool,address) external; + // Mocks a call to an address, returning specified data. + // Calldata can either be strict or a partial match, e.g. if you only + // pass a Solidity selector to the expected calldata, then the entire Solidity + // function will be mocked. + function mockCall(address,bytes calldata,bytes calldata) external; + // Mocks a call to an address with a specific msg.value, returning specified data. + // Calldata match takes precedence over msg.value in case of ambiguity. + function mockCall(address,uint256,bytes calldata,bytes calldata) external; + // Clears all mocked calls + function clearMockedCalls() external; + // Expects a call to an address with the specified calldata. + // Calldata can either be a strict or a partial match + function expectCall(address,bytes calldata) external; + // Expects a call to an address with the specified msg.value and calldata + function expectCall(address,uint256,bytes calldata) external; + // Gets the code from an artifact file. Takes in the relative path to the json file + function getCode(string calldata) external returns (bytes memory); + // Labels an address in call traces + function label(address, string calldata) external; + // If the condition is false, discard this run's fuzz inputs and generate new ones + function assume(bool) external; + // Sets block.coinbase (who) + function coinbase(address) external; + // Using the address that calls the test contract, has the next call (at this call depth only) create a transaction that can later be signed and sent onchain + function broadcast() external; + // Has the next call (at this call depth only) create a transaction with the address provided as the sender that can later be signed and sent onchain + function broadcast(address) external; + // Using the address that calls the test contract, has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain + function startBroadcast() external; + // Has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain + function startBroadcast(address) external; + // Stops collecting onchain transactions + function stopBroadcast() external; + + // Reads the entire content of file to string, (path) => (data) + function readFile(string calldata) external returns (string memory); + // Get the path of the current project root + function projectRoot() external returns (string memory); + // Reads next line of file to string, (path) => (line) + function readLine(string calldata) external returns (string memory); + // Writes data to file, creating a file if it does not exist, and entirely replacing its contents if it does. + // (path, data) => () + function writeFile(string calldata, string calldata) external; + // Writes line to file, creating a file if it does not exist. + // (path, data) => () + function writeLine(string calldata, string calldata) external; + // Closes file for reading, resetting the offset and allowing to read it from beginning with readLine. + // (path) => () + function closeFile(string calldata) external; + // Removes file. This cheatcode will revert in the following situations, but is not limited to just these cases: + // - Path points to a directory. + // - The file doesn't exist. + // - The user lacks permissions to remove the file. + // (path) => () + function removeFile(string calldata) external; + + // Convert values to a string, (value) => (stringified value) + function toString(address) external returns(string memory); + function toString(bytes calldata) external returns(string memory); + function toString(bytes32) external returns(string memory); + function toString(bool) external returns(string memory); + function toString(uint256) external returns(string memory); + function toString(int256) external returns(string memory); + + // Convert values from a string, (string) => (parsed value) + function parseBytes(string calldata) external returns (bytes memory); + function parseAddress(string calldata) external returns (address); + function parseUint(string calldata) external returns (uint256); + function parseInt(string calldata) external returns (int256); + function parseBytes32(string calldata) external returns (bytes32); + function parseBool(string calldata) external returns (bool); + + // Record all the transaction logs + function recordLogs() external; + // Gets all the recorded logs, () => (logs) + function getRecordedLogs() external returns (Log[] memory); + // Snapshot the current state of the evm. + // Returns the id of the snapshot that was created. + // To revert a snapshot use `revertTo` + function snapshot() external returns(uint256); + // Revert the state of the evm to a previous snapshot + // Takes the snapshot id to revert to. + // This deletes the snapshot and all snapshots taken after the given snapshot id. + function revertTo(uint256) external returns(bool); + + // Creates a new fork with the given endpoint and block and returns the identifier of the fork + function createFork(string calldata,uint256) external returns(uint256); + // Creates a new fork with the given endpoint and the _latest_ block and returns the identifier of the fork + function createFork(string calldata) external returns(uint256); + // Creates _and_ also selects a new fork with the given endpoint and block and returns the identifier of the fork + function createSelectFork(string calldata,uint256) external returns(uint256); + // Creates _and_ also selects a new fork with the given endpoint and the latest block and returns the identifier of the fork + function createSelectFork(string calldata) external returns(uint256); + // Takes a fork identifier created by `createFork` and sets the corresponding forked state as active. + function selectFork(uint256) external; + /// Returns the currently active fork + /// Reverts if no fork is currently active + function activeFork() external returns(uint256); + // Updates the currently active fork to given block number + // This is similar to `roll` but for the currently active fork + function rollFork(uint256) external; + // Updates the given fork to given block number + function rollFork(uint256 forkId, uint256 blockNumber) external; + + // Marks that the account(s) should use persistent storage across fork swaps in a multifork setup + // Meaning, changes made to the state of this account will be kept when switching forks + function makePersistent(address) external; + function makePersistent(address, address) external; + function makePersistent(address, address, address) external; + function makePersistent(address[] calldata) external; + // Revokes persistent status from the address, previously added via `makePersistent` + function revokePersistent(address) external; + function revokePersistent(address[] calldata) external; + // Returns true if the account is marked as persistent + function isPersistent(address) external returns (bool); + + // Returns the RPC url for the given alias + function rpcUrl(string calldata) external returns(string memory); + // Returns all rpc urls and their aliases `[alias, url][]` + function rpcUrls() external returns(string[2][] memory); + + // Derive a private key from a provided mnenomic string (or mnenomic file path) at the derivation path m/44'/60'/0'/0/{index} + function deriveKey(string calldata, uint32) external returns (uint256); + // Derive a private key from a provided mnenomic string (or mnenomic file path) at the derivation path {path}{index} + function deriveKey(string calldata, string calldata, uint32) external returns (uint256); + // parseJson + + // Given a string of JSON, return the ABI-encoded value of provided key + // (stringified json, key) => (ABI-encoded data) + // Read the note below! + function parseJson(string calldata, string calldata) external returns(bytes memory); + + // Given a string of JSON, return it as ABI-encoded, (stringified json, key) => (ABI-encoded data) + // Read the note below! + function parseJson(string calldata) external returns(bytes memory); + + // Note: + // ---- + // In case the returned value is a JSON object, it's encoded as a ABI-encoded tuple. As JSON objects + // don't have the notion of ordered, but tuples do, they JSON object is encoded with it's fields ordered in + // ALPHABETICAL ordser. That means that in order to succesfully decode the tuple, we need to define a tuple that + // encodes the fields in the same order, which is alphabetical. In the case of Solidity structs, they are encoded + // as tuples, with the attributes in the order in which they are defined. + // For example: json = { 'a': 1, 'b': 0xa4tb......3xs} + // a: uint256 + // b: address + // To decode that json, we need to define a struct or a tuple as follows: + // struct json = { uint256 a; address b; } + // If we defined a json struct with the opposite order, meaning placing the address b first, it would try to + // decode the tuple in that order, and thus fail. + +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/console.sol b/lib/openzeppelin-contracts/lib/forge-std/src/console.sol new file mode 100644 index 0000000..ad57e53 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/console.sol @@ -0,0 +1,1533 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +library console { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + /// @solidity memory-safe-assembly + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); + } + + function logUint(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); + } + + function log(uint p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); + } + + function log(uint p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); + } + + function log(uint p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); + } + + function log(string memory p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); + } + + function log(uint p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); + } + + function log(uint p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); + } + + function log(uint p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); + } + + function log(uint p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); + } + + function log(uint p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); + } + + function log(uint p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); + } + + function log(uint p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); + } + + function log(uint p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); + } + + function log(uint p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); + } + + function log(uint p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); + } + + function log(uint p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); + } + + function log(bool p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); + } + + function log(bool p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); + } + + function log(bool p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); + } + + function log(address p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); + } + + function log(address p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); + } + + function log(address p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/console2.sol b/lib/openzeppelin-contracts/lib/forge-std/src/console2.sol new file mode 100644 index 0000000..2edfda5 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/console2.sol @@ -0,0 +1,1538 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.9.0; + +// The orignal console.sol uses `int` and `uint` for computing function selectors, but it should +// use `int256` and `uint256`. This modified version fixes that. This version is recommended +// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in +// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`. +// Reference: https://github.com/NomicFoundation/hardhat/issues/2178 + +library console2 { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); + } + + function logUint(uint256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint256 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint256 p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); + } + + function log(uint256 p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); + } + + function log(uint256 p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); + } + + function log(uint256 p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); + } + + function log(string memory p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint256 p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint256 p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); + } + + function log(uint256 p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); + } + + function log(uint256 p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); + } + + function log(uint256 p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); + } + + function log(bool p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); + } + + function log(address p0, uint256 p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint256 p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint256 p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint256 p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint256 p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint256 p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/Script.t.sol b/lib/openzeppelin-contracts/lib/forge-std/src/test/Script.t.sol new file mode 100644 index 0000000..595df7b --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/Script.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract ScriptTest is Test +{ + function testGenerateCorrectAddress() external { + address creation = computeCreateAddress(0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9, 14); + assertEq(creation, 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45); + } +} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/StdAssertions.t.sol b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdAssertions.t.sol new file mode 100644 index 0000000..348ce21 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdAssertions.t.sol @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract StdAssertionsTest is Test +{ + string constant CUSTOM_ERROR = "guh!"; + + bool constant EXPECT_PASS = false; + bool constant EXPECT_FAIL = true; + + TestTest t = new TestTest(); + + /*////////////////////////////////////////////////////////////////////////// + FAIL(STRING) + //////////////////////////////////////////////////////////////////////////*/ + + function testShouldFail() external { + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._fail(CUSTOM_ERROR); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_FALSE + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertFalse_Pass() external { + t._assertFalse(false, EXPECT_PASS); + } + + function testAssertFalse_Fail() external { + vm.expectEmit(false, false, false, true); + emit log("Error: Assertion Failed"); + t._assertFalse(true, EXPECT_FAIL); + } + + function testAssertFalse_Err_Pass() external { + t._assertFalse(false, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertFalse_Err_Fail() external { + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertFalse(true, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(BOOL) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_Bool_Pass(bool a) external { + t._assertEq(a, a, EXPECT_PASS); + } + + function testAssertEq_Bool_Fail(bool a, bool b) external { + vm.assume(a != b); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [bool]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_BoolErr_Pass(bool a) external { + t._assertEq(a, a, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertEq_BoolErr_Fail(bool a, bool b) external { + vm.assume(a != b); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(BYTES) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_Bytes_Pass(bytes calldata a) external { + t._assertEq(a, a, EXPECT_PASS); + } + + function testAssertEq_Bytes_Fail(bytes calldata a, bytes calldata b) external { + vm.assume(keccak256(a) != keccak256(b)); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [bytes]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_BytesErr_Pass(bytes calldata a) external { + t._assertEq(a, a, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertEq_BytesErr_Fail(bytes calldata a, bytes calldata b) external { + vm.assume(keccak256(a) != keccak256(b)); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + ASSERT_EQ(ARRAY) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertEq_UintArr_Pass(uint256 e0, uint256 e1, uint256 e2) public { + uint256[] memory a = new uint256[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + uint256[] memory b = new uint256[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_IntArr_Pass(int256 e0, int256 e1, int256 e2) public { + int256[] memory a = new int256[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + int256[] memory b = new int256[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_AddressArr_Pass(address e0, address e1, address e2) public { + address[] memory a = new address[](3); + a[0] = e0; + a[1] = e1; + a[2] = e2; + address[] memory b = new address[](3); + b[0] = e0; + b[1] = e1; + b[2] = e2; + + t._assertEq(a, b, EXPECT_PASS); + } + + function testAssertEq_UintArr_FailEl(uint256 e1) public { + vm.assume(e1 != 0); + uint256[] memory a = new uint256[](3); + uint256[] memory b = new uint256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_IntArr_FailEl(int256 e1) public { + vm.assume(e1 != 0); + int256[] memory a = new int256[](3); + int256[] memory b = new int256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + + function testAssertEq_AddressArr_FailEl(address e1) public { + vm.assume(e1 != address(0)); + address[] memory a = new address[](3); + address[] memory b = new address[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_UintArrErr_FailEl(uint256 e1) public { + vm.assume(e1 != 0); + uint256[] memory a = new uint256[](3); + uint256[] memory b = new uint256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_IntArrErr_FailEl(int256 e1) public { + vm.assume(e1 != 0); + int256[] memory a = new int256[](3); + int256[] memory b = new int256[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + + function testAssertEq_AddressArrErr_FailEl(address e1) public { + vm.assume(e1 != address(0)); + address[] memory a = new address[](3); + address[] memory b = new address[](3); + b[1] = e1; + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_UintArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + uint256[] memory a = new uint256[](lenA); + uint256[] memory b = new uint256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_IntArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + int256[] memory a = new int256[](lenA); + int256[] memory b = new int256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_AddressArr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + address[] memory a = new address[](lenA); + address[] memory b = new address[](lenB); + + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, EXPECT_FAIL); + } + + function testAssertEq_UintArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + uint256[] memory a = new uint256[](lenA); + uint256[] memory b = new uint256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [uint[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_IntArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + int256[] memory a = new int256[](lenA); + int256[] memory b = new int256[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [int[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + function testAssertEq_AddressArrErr_FailLen(uint256 lenA, uint256 lenB) public { + vm.assume(lenA != lenB); + vm.assume(lenA <= 10000); + vm.assume(lenB <= 10000); + address[] memory a = new address[](lenA); + address[] memory b = new address[](lenB); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + vm.expectEmit(false, false, false, true); + emit log("Error: a == b not satisfied [address[]]"); + t._assertEq(a, b, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_ABS(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqAbs_Uint_Pass(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_PASS); + } + + function testAssertApproxEqAbs_Uint_Fail(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [uint]"); + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_FAIL); + } + + function testAssertApproxEqAbs_UintErr_Pass(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqAbs_UintErr_Fail(uint256 a, uint256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_ABS(INT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqAbs_Int_Pass(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_PASS); + } + + function testAssertApproxEqAbs_Int_Fail(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [int]"); + t._assertApproxEqAbs(a, b, maxDelta, EXPECT_FAIL); + } + + function testAssertApproxEqAbs_IntErr_Pass(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) <= maxDelta); + + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqAbs_IntErr_Fail(int256 a, int256 b, uint256 maxDelta) external { + vm.assume(stdMath.delta(a, b) > maxDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqAbs(a, b, maxDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_REL(UINT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqRel_Uint_Pass(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_PASS); + } + + function testAssertApproxEqRel_Uint_Fail(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [uint]"); + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_FAIL); + } + + function testAssertApproxEqRel_UintErr_Pass(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqRel_UintErr_Fail(uint256 a, uint256 b, uint256 maxPercentDelta) external { + vm.assume(a < type(uint128).max && b < type(uint128).max && b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_FAIL); + } + + /*////////////////////////////////////////////////////////////////////////// + APPROX_EQ_REL(INT) + //////////////////////////////////////////////////////////////////////////*/ + + function testAssertApproxEqRel_Int_Pass(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_PASS); + } + + function testAssertApproxEqRel_Int_Fail(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log("Error: a ~= b not satisfied [int]"); + t._assertApproxEqRel(a, b, maxPercentDelta, EXPECT_FAIL); + } + + function testAssertApproxEqRel_IntErr_Pass(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) <= maxPercentDelta); + + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_PASS); + } + + function testAssertApproxEqRel_IntErr_Fail(int128 a, int128 b, uint128 maxPercentDelta) external { + vm.assume(b != 0); + vm.assume(stdMath.percentDelta(a, b) > maxPercentDelta); + + vm.expectEmit(false, false, false, true); + emit log_named_string("Error", CUSTOM_ERROR); + t._assertApproxEqRel(a, b, maxPercentDelta, CUSTOM_ERROR, EXPECT_FAIL); + } +} + + +contract TestTest is Test +{ + modifier expectFailure(bool expectFail) { + bool preState = vm.load(HEVM_ADDRESS, bytes32("failed")) != bytes32(0x00); + _; + bool postState = vm.load(HEVM_ADDRESS, bytes32("failed")) != bytes32(0x00); + + if (preState == true) { + return; + } + + if (expectFail) { + require(postState == true, "expected failure not triggered"); + + // unwind the expected failure + vm.store(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x00))); + } else { + require(postState == false, "unexpected failure was triggered"); + } + } + + function _fail(string memory err) external expectFailure(true) { + fail(err); + } + + function _assertFalse(bool data, bool expectFail) external expectFailure(expectFail) { + assertFalse(data); + } + + function _assertFalse(bool data, string memory err, bool expectFail) external expectFailure(expectFail) { + assertFalse(data, err); + } + + function _assertEq(bool a, bool b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(bool a, bool b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(bytes memory a, bytes memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(bytes memory a, + bytes memory b, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(uint256[] memory a, uint256[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(int256[] memory a, int256[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(address[] memory a, address[] memory b, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b); + } + + function _assertEq(uint256[] memory a, uint256[] memory b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(int256[] memory a, int256[] memory b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + function _assertEq(address[] memory a, address[] memory b, string memory err, bool expectFail) external expectFailure(expectFail) { + assertEq(a, b, err); + } + + + function _assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta); + } + + function _assertApproxEqAbs( + uint256 a, + uint256 b, + uint256 maxDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta, err); + } + + function _assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta); + } + + function _assertApproxEqAbs( + int256 a, + int256 b, + uint256 maxDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqAbs(a, b, maxDelta, err); + } + + function _assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta); + } + + function _assertApproxEqRel( + uint256 a, + uint256 b, + uint256 maxPercentDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta, err); + } + + function _assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta); + } + + function _assertApproxEqRel( + int256 a, + int256 b, + uint256 maxPercentDelta, + string memory err, + bool expectFail + ) external expectFailure(expectFail) { + assertApproxEqRel(a, b, maxPercentDelta, err); + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/StdCheats.t.sol b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdCheats.t.sol new file mode 100644 index 0000000..05e240a --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdCheats.t.sol @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; +import "../StdJson.sol"; + +contract StdCheatsTest is Test { + Bar test; + + using stdJson for string; + + function setUp() public { + test = new Bar(); + } + + function testSkip() public { + vm.warp(100); + skip(25); + assertEq(block.timestamp, 125); + } + + function testRewind() public { + vm.warp(100); + rewind(25); + assertEq(block.timestamp, 75); + } + + function testHoax() public { + hoax(address(1337)); + test.bar{value: 100}(address(1337)); + } + + function testHoaxOrigin() public { + hoax(address(1337), address(1337)); + test.origin{value: 100}(address(1337)); + } + + function testHoaxDifferentAddresses() public { + hoax(address(1337), address(7331)); + test.origin{value: 100}(address(1337), address(7331)); + } + + function testStartHoax() public { + startHoax(address(1337)); + test.bar{value: 100}(address(1337)); + test.bar{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } + + function testStartHoaxOrigin() public { + startHoax(address(1337), address(1337)); + test.origin{value: 100}(address(1337)); + test.origin{value: 100}(address(1337)); + vm.stopPrank(); + test.bar(address(this)); + } + + function testChangePrank() public { + vm.startPrank(address(1337)); + test.bar(address(1337)); + changePrank(address(0xdead)); + test.bar(address(0xdead)); + changePrank(address(1337)); + test.bar(address(1337)); + vm.stopPrank(); + } + + function testMakeAddrEquivalence() public { + (address addr, ) = makeAddrAndKey("1337"); + assertEq(makeAddr("1337"), addr); + } + + function testMakeAddrSigning() public { + (address addr, uint256 key) = makeAddrAndKey("1337"); + bytes32 hash = keccak256("some_message"); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(key, hash); + assertEq(ecrecover(hash, v, r, s), addr); + } + + function testDeal() public { + deal(address(this), 1 ether); + assertEq(address(this).balance, 1 ether); + } + + function testDealToken() public { + Bar barToken = new Bar(); + address bar = address(barToken); + deal(bar, address(this), 10000e18); + assertEq(barToken.balanceOf(address(this)), 10000e18); + } + + function testDealTokenAdjustTS() public { + Bar barToken = new Bar(); + address bar = address(barToken); + deal(bar, address(this), 10000e18, true); + assertEq(barToken.balanceOf(address(this)), 10000e18); + assertEq(barToken.totalSupply(), 20000e18); + deal(bar, address(this), 0, true); + assertEq(barToken.balanceOf(address(this)), 0); + assertEq(barToken.totalSupply(), 10000e18); + } + + function testBound() public { + assertEq(bound(5, 0, 4), 0); + assertEq(bound(0, 69, 69), 69); + assertEq(bound(0, 68, 69), 68); + assertEq(bound(10, 150, 190), 160); + assertEq(bound(300, 2800, 3200), 3100); + assertEq(bound(9999, 1337, 6666), 6006); + } + + function testCannotBoundMaxLessThanMin() public { + vm.expectRevert(bytes("Test bound(uint256,uint256,uint256): Max is less than min.")); + bound(5, 100, 10); + } + + function testBound( + uint256 num, + uint256 min, + uint256 max + ) public { + if (min > max) (min, max) = (max, min); + + uint256 bounded = bound(num, min, max); + + assertGe(bounded, min); + assertLe(bounded, max); + } + + function testBoundUint256Max() public { + assertEq(bound(0, type(uint256).max - 1, type(uint256).max), type(uint256).max - 1); + assertEq(bound(1, type(uint256).max - 1, type(uint256).max), type(uint256).max); + } + + function testCannotBoundMaxLessThanMin( + uint256 num, + uint256 min, + uint256 max + ) public { + vm.assume(min > max); + vm.expectRevert(bytes("Test bound(uint256,uint256,uint256): Max is less than min.")); + bound(num, min, max); + } + + function testDeployCode() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", bytes("")); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + } + + function testDeployCodeNoArgs() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest"); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + } + + // We need that payable constructor in order to send ETH on construction + constructor() payable {} + + function testDeployCodeVal() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", bytes(""), 1 ether); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + assertEq(deployed.balance, 1 ether); + } + + function testDeployCodeValNoArgs() public { + address deployed = deployCode("StdCheats.t.sol:StdCheatsTest", 1 ether); + assertEq(string(getCode(deployed)), string(getCode(address(this)))); + assertEq(deployed.balance, 1 ether); + } + + // We need this so we can call "this.deployCode" rather than "deployCode" directly + function deployCodeHelper(string memory what) external { + deployCode(what); + } + + function testDeployCodeFail() public { + vm.expectRevert(bytes("Test deployCode(string): Deployment failed.")); + this.deployCodeHelper("StdCheats.t.sol:RevertingContract"); + } + + function getCode(address who) internal view returns (bytes memory o_code) { + /// @solidity memory-safe-assembly + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(who) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(who, add(o_code, 0x20), 0, size) + } + } + + function testBytesToUint() public { + assertEq(3, bytesToUint(hex'03')); + assertEq(2, bytesToUint(hex'02')); + assertEq(255, bytesToUint(hex'ff')); + assertEq(29625, bytesToUint(hex'73b9')); + } + + function testParseJsonTxDetail() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + string memory json = vm.readFile(path); + bytes memory transactionDetails = json.parseRaw(".transactions[0].tx"); + RawTx1559Detail memory rawTxDetail = abi.decode(transactionDetails, (RawTx1559Detail)); + Tx1559Detail memory txDetail = rawToConvertedEIP1559Detail(rawTxDetail); + assertEq(txDetail.from, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); + assertEq(txDetail.to, 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512); + assertEq(txDetail.data, hex'23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004'); + assertEq(txDetail.nonce, 3); + assertEq(txDetail.txType, 2); + assertEq(txDetail.gas, 29625); + assertEq(txDetail.value, 0); + } + + function testReadEIP1559Transaction() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + uint256 index = 0; + Tx1559 memory transaction = readTx1559(path, index); + } + + function testReadEIP1559Transactions() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + Tx1559[] memory transactions = readTx1559s(path); + } + + function testReadReceipt() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + uint index = 5; + Receipt memory receipt = readReceipt(path, index); + assertEq(receipt.logsBloom, + hex"00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100"); + } + + function testReadReceipts() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/src/test/fixtures/broadcast.log.json"); + Receipt[] memory receipts = readReceipts(path); + } + +} + +contract Bar { + constructor() { + /// `DEAL` STDCHEAT + totalSupply = 10000e18; + balanceOf[address(this)] = totalSupply; + } + + /// `HOAX` STDCHEATS + function bar(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + } + function origin(address expectedSender) public payable { + require(msg.sender == expectedSender, "!prank"); + require(tx.origin == expectedSender, "!prank"); + } + function origin(address expectedSender, address expectedOrigin) public payable { + require(msg.sender == expectedSender, "!prank"); + require(tx.origin == expectedOrigin, "!prank"); + } + + /// `DEAL` STDCHEAT + mapping (address => uint256) public balanceOf; + uint256 public totalSupply; +} + +contract RevertingContract { + constructor() { + revert(); + } +} + diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/StdError.t.sol b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdError.t.sol new file mode 100644 index 0000000..0d6601e --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdError.t.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.10 <0.9.0; + +import "../Test.sol"; + +contract StdErrorsTest is Test { + ErrorsTest test; + + function setUp() public { + test = new ErrorsTest(); + } + + function testExpectAssertion() public { + vm.expectRevert(stdError.assertionError); + test.assertionError(); + } + + function testExpectArithmetic() public { + vm.expectRevert(stdError.arithmeticError); + test.arithmeticError(10); + } + + function testExpectDiv() public { + vm.expectRevert(stdError.divisionError); + test.divError(0); + } + + function testExpectMod() public { + vm.expectRevert(stdError.divisionError); + test.modError(0); + } + + function testExpectEnum() public { + vm.expectRevert(stdError.enumConversionError); + test.enumConversion(1); + } + + function testExpectEncodeStg() public { + vm.expectRevert(stdError.encodeStorageError); + test.encodeStgError(); + } + + function testExpectPop() public { + vm.expectRevert(stdError.popError); + test.pop(); + } + + function testExpectOOB() public { + vm.expectRevert(stdError.indexOOBError); + test.indexOOBError(1); + } + + function testExpectMem() public { + vm.expectRevert(stdError.memOverflowError); + test.mem(); + } + + function testExpectIntern() public { + vm.expectRevert(stdError.zeroVarError); + test.intern(); + } + + function testExpectLowLvl() public { + vm.expectRevert(stdError.lowLevelError); + test.someArr(0); + } +} + +contract ErrorsTest { + enum T { + T1 + } + + uint256[] public someArr; + bytes someBytes; + + function assertionError() public pure { + assert(false); + } + + function arithmeticError(uint256 a) public pure { + a -= 100; + } + + function divError(uint256 a) public pure { + 100 / a; + } + + function modError(uint256 a) public pure { + 100 % a; + } + + function enumConversion(uint256 a) public pure { + T(a); + } + + function encodeStgError() public { + /// @solidity memory-safe-assembly + assembly { + sstore(someBytes.slot, 1) + } + keccak256(someBytes); + } + + function pop() public { + someArr.pop(); + } + + function indexOOBError(uint256 a) public pure { + uint256[] memory t = new uint256[](0); + t[a]; + } + + function mem() public pure { + uint256 l = 2**256 / 32; + new uint256[](l); + } + + function intern() public returns (uint256) { + function(uint256) internal returns (uint256) x; + x(2); + return 7; + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/StdMath.t.sol b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdMath.t.sol new file mode 100644 index 0000000..9d09b81 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdMath.t.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +import "../Test.sol"; + +contract StdMathTest is Test +{ + function testGetAbs() external { + assertEq(stdMath.abs(-50), 50); + assertEq(stdMath.abs(50), 50); + assertEq(stdMath.abs(-1337), 1337); + assertEq(stdMath.abs(0), 0); + + assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1); + assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1)); + } + + function testGetAbs_Fuzz(int256 a) external { + uint256 manualAbs = getAbs(a); + + uint256 abs = stdMath.abs(a); + + assertEq(abs, manualAbs); + } + + function testGetDelta_Uint() external { + assertEq(stdMath.delta(uint256(0), uint256(0)), 0); + assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337); + assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max); + assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max); + assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max); + + assertEq(stdMath.delta(0, uint256(0)), 0); + assertEq(stdMath.delta(1337, uint256(0)), 1337); + assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max); + assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max); + assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max); + + assertEq(stdMath.delta(1337, uint256(1337)), 0); + assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0); + assertEq(stdMath.delta(5000, uint256(1250)), 3750); + } + + function testGetDelta_Uint_Fuzz(uint256 a, uint256 b) external { + uint256 manualDelta; + if (a > b) { + manualDelta = a - b; + } else { + manualDelta = b - a; + } + + uint256 delta = stdMath.delta(a, b); + + assertEq(delta, manualDelta); + } + + function testGetDelta_Int() external { + assertEq(stdMath.delta(int256(0), int256(0)), 0); + assertEq(stdMath.delta(int256(0), int256(1337)), 1337); + assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1); + assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1); + assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1); + + assertEq(stdMath.delta(0, int256(0)), 0); + assertEq(stdMath.delta(1337, int256(0)), 1337); + assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1); + assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1); + assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1); + + assertEq(stdMath.delta(-0, int256(0)), 0); + assertEq(stdMath.delta(-1337, int256(0)), 1337); + assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1); + assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1); + assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1); + + assertEq(stdMath.delta(int256(0), -0), 0); + assertEq(stdMath.delta(int256(0), -1337), 1337); + assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1); + assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1); + assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1); + + assertEq(stdMath.delta(1337, int256(1337)), 0); + assertEq(stdMath.delta(type(int256).max, type(int256).max), 0); + assertEq(stdMath.delta(type(int256).min, type(int256).min), 0); + assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max); + assertEq(stdMath.delta(5000, int256(1250)), 3750); + } + + function testGetDelta_Int_Fuzz(int256 a, int256 b) external { + uint256 absA = getAbs(a); + uint256 absB = getAbs(b); + uint256 absDelta = absA > absB + ? absA - absB + : absB - absA; + + uint256 manualDelta; + if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { + manualDelta = absDelta; + } + // (a < 0 && b >= 0) || (a >= 0 && b < 0) + else { + manualDelta = absA + absB; + } + + uint256 delta = stdMath.delta(a, b); + + assertEq(delta, manualDelta); + } + + function testGetPercentDelta_Uint() external { + assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18); + assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18); + + assertEq(stdMath.percentDelta(1337, uint256(1337)), 0); + assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0); + assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18); + assertEq(stdMath.percentDelta(2500, uint256(2500)), 0); + assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18); + assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18); + + vm.expectRevert(stdError.divisionError); + stdMath.percentDelta(uint256(1), 0); + } + + function testGetPercentDelta_Uint_Fuzz(uint192 a, uint192 b) external { + vm.assume(b != 0); + uint256 manualDelta; + if (a > b) { + manualDelta = a - b; + } else { + manualDelta = b - a; + } + + uint256 manualPercentDelta = manualDelta * 1e18 / b; + uint256 percentDelta = stdMath.percentDelta(a, b); + + assertEq(percentDelta, manualPercentDelta); + } + + function testGetPercentDelta_Int() external { + assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18); + assertEq(stdMath.percentDelta(int256(0), -1337), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18); + assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18); + + assertEq(stdMath.percentDelta(1337, int256(1337)), 0); + assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0); + assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0); + + assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down + assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down + assertEq(stdMath.percentDelta(0, int256(2500)), 1e18); + assertEq(stdMath.percentDelta(2500, int256(2500)), 0); + assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18); + assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18); + + vm.expectRevert(stdError.divisionError); + stdMath.percentDelta(int256(1), 0); + } + + function testGetPercentDelta_Int_Fuzz(int192 a, int192 b) external { + vm.assume(b != 0); + uint256 absA = getAbs(a); + uint256 absB = getAbs(b); + uint256 absDelta = absA > absB + ? absA - absB + : absB - absA; + + uint256 manualDelta; + if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) { + manualDelta = absDelta; + } + // (a < 0 && b >= 0) || (a >= 0 && b < 0) + else { + manualDelta = absA + absB; + } + + uint256 manualPercentDelta = manualDelta * 1e18 / absB; + uint256 percentDelta = stdMath.percentDelta(a, b); + + assertEq(percentDelta, manualPercentDelta); + } + + /*////////////////////////////////////////////////////////////////////////// + HELPERS + //////////////////////////////////////////////////////////////////////////*/ + + function getAbs(int256 a) private pure returns (uint256) { + if (a < 0) + return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a); + + return uint256(a); + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/StdStorage.t.sol b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdStorage.t.sol new file mode 100644 index 0000000..6e238d0 --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/StdStorage.t.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0 <0.9.0; + +import "../Test.sol"; + +contract StdStorageTest is Test { + using stdStorage for StdStorage; + + StorageTest test; + + function setUp() public { + test = new StorageTest(); + } + + function testStorageHidden() public { + assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find()); + } + + function testStorageObvious() public { + assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find()); + } + + function testStorageCheckedWriteHidden() public { + stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100); + assertEq(uint256(test.hidden()), 100); + } + + function testStorageCheckedWriteObvious() public { + stdstore.target(address(test)).sig(test.exists.selector).checked_write(100); + assertEq(test.exists(), 100); + } + + function testStorageMapStructA() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(0) + .find(); + assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot); + } + + function testStorageMapStructB() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(1) + .find(); + assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot); + } + + function testStorageDeepMap() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.deep_map.selector) + .with_key(address(this)) + .with_key(address(this)) + .find(); + assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint(5)))))), slot); + } + + function testStorageCheckedWriteDeepMap() public { + stdstore + .target(address(test)) + .sig(test.deep_map.selector) + .with_key(address(this)) + .with_key(address(this)) + .checked_write(100); + assertEq(100, test.deep_map(address(this), address(this))); + } + + function testStorageDeepMapStructA() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(0) + .find(); + assertEq(bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint(6)))))) + 0), bytes32(slot)); + } + + function testStorageDeepMapStructB() public { + uint256 slot = stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(1) + .find(); + assertEq(bytes32(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint(6)))))) + 1), bytes32(slot)); + } + + function testStorageCheckedWriteDeepMapStructA() public { + stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(0) + .checked_write(100); + (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); + assertEq(100, a); + assertEq(0, b); + } + + function testStorageCheckedWriteDeepMapStructB() public { + stdstore + .target(address(test)) + .sig(test.deep_map_struct.selector) + .with_key(address(this)) + .with_key(address(this)) + .depth(1) + .checked_write(100); + (uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this)); + assertEq(0, a); + assertEq(100, b); + } + + function testStorageCheckedWriteMapStructA() public { + stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(0) + .checked_write(100); + (uint256 a, uint256 b) = test.map_struct(address(this)); + assertEq(a, 100); + assertEq(b, 0); + } + + function testStorageCheckedWriteMapStructB() public { + stdstore + .target(address(test)) + .sig(test.map_struct.selector) + .with_key(address(this)) + .depth(1) + .checked_write(100); + (uint256 a, uint256 b) = test.map_struct(address(this)); + assertEq(a, 0); + assertEq(b, 100); + } + + function testStorageStructA() public { + uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find(); + assertEq(uint256(7), slot); + } + + function testStorageStructB() public { + uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find(); + assertEq(uint256(7) + 1, slot); + } + + function testStorageCheckedWriteStructA() public { + stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100); + (uint256 a, uint256 b) = test.basic(); + assertEq(a, 100); + assertEq(b, 1337); + } + + function testStorageCheckedWriteStructB() public { + stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100); + (uint256 a, uint256 b) = test.basic(); + assertEq(a, 1337); + assertEq(b, 100); + } + + function testStorageMapAddrFound() public { + uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find(); + assertEq(uint256(keccak256(abi.encode(address(this), uint(1)))), slot); + } + + function testStorageMapUintFound() public { + uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find(); + assertEq(uint256(keccak256(abi.encode(100, uint(2)))), slot); + } + + function testStorageCheckedWriteMapUint() public { + stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100); + assertEq(100, test.map_uint(100)); + } + + function testStorageCheckedWriteMapAddr() public { + stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100); + assertEq(100, test.map_addr(address(this))); + } + + function testStorageCheckedWriteMapBool() public { + stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true); + assertTrue(test.map_bool(address(this))); + } + + function testFailStorageCheckedWriteMapPacked() public { + // expect PackedSlot error but not external call so cant expectRevert + stdstore.target(address(test)).sig(test.read_struct_lower.selector).with_key(address(uint160(1337))).checked_write(100); + } + + function testStorageCheckedWriteMapPackedSuccess() public { + uint256 full = test.map_packed(address(1337)); + // keep upper 128, set lower 128 to 1337 + full = (full & (uint256((1 << 128) - 1) << 128)) | 1337; + stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337))).checked_write(full); + assertEq(1337, test.read_struct_lower(address(1337))); + } + + function testFailStorageConst() public { + // vm.expectRevert(abi.encodeWithSignature("NotStorage(bytes4)", bytes4(keccak256("const()")))); + stdstore.target(address(test)).sig("const()").find(); + } + + function testFailStorageNativePack() public { + stdstore.target(address(test)).sig(test.tA.selector).find(); + stdstore.target(address(test)).sig(test.tB.selector).find(); + + // these both would fail + stdstore.target(address(test)).sig(test.tC.selector).find(); + stdstore.target(address(test)).sig(test.tD.selector).find(); + } + + function testStorageReadBytes32() public { + bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32(); + assertEq(val, hex"1337"); + } + + function testStorageReadBool_False() public { + bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool(); + assertEq(val, false); + } + + function testStorageReadBool_True() public { + bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool(); + assertEq(val, true); + } + + function testStorageReadBool_Revert() public { + vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool."); + this.readNonBoolValue(); + } + + function readNonBoolValue() public { + stdstore.target(address(test)).sig(test.tE.selector).read_bool(); + } + + function testStorageReadAddress() public { + address val = stdstore.target(address(test)).sig(test.tF.selector).read_address(); + assertEq(val, address(1337)); + } + + function testStorageReadUint() public { + uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint(); + assertEq(val, 1); + } + + function testStorageReadInt() public { + int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int(); + assertEq(val, type(int256).min); + } +} + +contract StorageTest { + uint256 public exists = 1; + mapping(address => uint256) public map_addr; + mapping(uint256 => uint256) public map_uint; + mapping(address => uint256) public map_packed; + mapping(address => UnpackedStruct) public map_struct; + mapping(address => mapping(address => uint256)) public deep_map; + mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct; + UnpackedStruct public basic; + + uint248 public tA; + bool public tB; + + + bool public tC = false; + uint248 public tD = 1; + + + struct UnpackedStruct { + uint256 a; + uint256 b; + } + + mapping(address => bool) public map_bool; + + bytes32 public tE = hex"1337"; + address public tF = address(1337); + int256 public tG = type(int256).min; + bool public tH = true; + + constructor() { + basic = UnpackedStruct({ + a: 1337, + b: 1337 + }); + + uint256 two = (1<<128) | 1; + map_packed[msg.sender] = two; + map_packed[address(bytes20(uint160(1337)))] = 1<<128; + } + + function read_struct_upper(address who) public view returns (uint256) { + return map_packed[who] >> 128; + } + + function read_struct_lower(address who) public view returns (uint256) { + return map_packed[who] & ((1 << 128) - 1); + } + + function hidden() public view returns (bytes32 t) { + bytes32 slot = keccak256("my.random.var"); + /// @solidity memory-safe-assembly + assembly { + t := sload(slot) + } + } + + function const() public pure returns (bytes32 t) { + t = bytes32(hex"1337"); + } +} diff --git a/lib/openzeppelin-contracts/lib/forge-std/src/test/fixtures/broadcast.log.json b/lib/openzeppelin-contracts/lib/forge-std/src/test/fixtures/broadcast.log.json new file mode 100644 index 0000000..0a0200b --- /dev/null +++ b/lib/openzeppelin-contracts/lib/forge-std/src/test/fixtures/broadcast.log.json @@ -0,0 +1,187 @@ +{ + "transactions": [ + { + "hash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "function": "multiple_arguments(uint256,address,uint256[]):(uint256)", + "arguments": ["1", "0000000000000000000000000000000000001337", "[3,4]"], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "gas": "0x73b9", + "value": "0x0", + "data": "0x23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004", + "nonce": "0x3", + "accessList": [] + } + }, + { + "hash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "function": "inc():(uint256)", + "arguments": [], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "gas": "0xdcb2", + "value": "0x0", + "data": "0x371303c0", + "nonce": "0x4", + "accessList": [] + } + }, + { + "hash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "type": "CALL", + "contractName": "Test", + "contractAddress": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "function": "t(uint256):(uint256)", + "arguments": ["1"], + "tx": { + "type": "0x02", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "gas": "0x8599", + "value": "0x0", + "data": "0xafe29f710000000000000000000000000000000000000000000000000000000000000001", + "nonce": "0x5", + "accessList": [] + } + } + ], + "receipts": [ + { + "transactionHash": "0x481dc86e40bba90403c76f8e144aa9ff04c1da2164299d0298573835f0991181", + "transactionIndex": "0x0", + "blockHash": "0xef0730448490304e5403be0fa8f8ce64f118e9adcca60c07a2ae1ab921d748af", + "blockNumber": "0x1", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "cumulativeGasUsed": "0x13f3a", + "gasUsed": "0x13f3a", + "contractAddress": "0x5fbdb2315678afecb367f032d93f642f64180aa3", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x6a187183545b8a9e7f1790e847139379bf5622baff2cb43acf3f5c79470af782", + "transactionIndex": "0x0", + "blockHash": "0xf3acb96a90071640c2a8c067ae4e16aad87e634ea8d8bbbb5b352fba86ba0148", + "blockNumber": "0x2", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": null, + "cumulativeGasUsed": "0x45d80", + "gasUsed": "0x45d80", + "contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x064ad173b4867bdef2fb60060bbdaf01735fbf10414541ea857772974e74ea9d", + "transactionIndex": "0x0", + "blockHash": "0x8373d02109d3ee06a0225f23da4c161c656ccc48fe0fcee931d325508ae73e58", + "blockNumber": "0x3", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", + "cumulativeGasUsed": "0x45feb", + "gasUsed": "0x45feb", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f", + "transactionIndex": "0x0", + "blockHash": "0x16712fae5c0e18f75045f84363fb6b4d9a9fe25e660c4ce286833a533c97f629", + "blockNumber": "0x4", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "cumulativeGasUsed": "0x5905", + "gasUsed": "0x5905", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298", + "transactionIndex": "0x0", + "blockHash": "0x156b88c3eb9a1244ba00a1834f3f70de735b39e3e59006dd03af4fe7d5480c11", + "blockNumber": "0x5", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + "cumulativeGasUsed": "0xa9c4", + "gasUsed": "0xa9c4", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "transactionIndex": "0x0", + "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", + "blockNumber": "0x6", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "cumulativeGasUsed": "0x66c5", + "gasUsed": "0x66c5", + "contractAddress": null, + "logs": [ + { + "address": "0x7c6b4bbe207d642d98d5c537142d85209e585087", + "topics": [ + "0x0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b" + ], + "data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000", + "blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb", + "blockNumber": "0x6", + "transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c", + "transactionIndex": "0x1", + "logIndex": "0x0", + "transactionLogIndex": "0x0", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100", + "effectiveGasPrice": "0xee6b2800" + }, + { + "transactionHash": "0x11fbb10230c168ca1e36a7e5c69a6dbcd04fd9e64ede39d10a83e36ee8065c16", + "transactionIndex": "0x0", + "blockHash": "0xf1e0ed2eda4e923626ec74621006ed50b3fc27580dc7b4cf68a07ca77420e29c", + "blockNumber": "0x7", + "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "to": "0x0000000000000000000000000000000000001337", + "cumulativeGasUsed": "0x5208", + "gasUsed": "0x5208", + "contractAddress": null, + "logs": [], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "effectiveGasPrice": "0xee6b2800" + } + ], + "libraries": [ + "src/Broadcast.t.sol:F:0x5fbdb2315678afecb367f032d93f642f64180aa3" + ], + "pending": [], + "path": "broadcast/Broadcast.t.sol/31337/run-latest.json", + "returns": {}, + "timestamp": 1655140035 +} diff --git a/lib/openzeppelin-contracts/logo.svg b/lib/openzeppelin-contracts/logo.svg new file mode 100644 index 0000000..f1e14c2 --- /dev/null +++ b/lib/openzeppelin-contracts/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/lib/openzeppelin-contracts/netlify.toml b/lib/openzeppelin-contracts/netlify.toml new file mode 100644 index 0000000..0447f41 --- /dev/null +++ b/lib/openzeppelin-contracts/netlify.toml @@ -0,0 +1,3 @@ +[build] +command = "npm run docs" +publish = "build/site" diff --git a/lib/openzeppelin-contracts/package-lock.json b/lib/openzeppelin-contracts/package-lock.json new file mode 100644 index 0000000..8d8a2ca --- /dev/null +++ b/lib/openzeppelin-contracts/package-lock.json @@ -0,0 +1,26461 @@ +{ + "name": "openzeppelin-solidity", + "version": "4.8.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "openzeppelin-solidity", + "version": "4.8.0", + "license": "MIT", + "bin": { + "openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js" + }, + "devDependencies": { + "@nomicfoundation/hardhat-network-helpers": "^1.0.3", + "@nomiclabs/hardhat-truffle5": "^2.0.5", + "@nomiclabs/hardhat-web3": "^2.0.0", + "@openzeppelin/docs-utils": "^0.1.3", + "@openzeppelin/test-helpers": "^0.5.13", + "chai": "^4.2.0", + "eslint": "^7.32.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-mocha": "^10.0.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.2.0", + "eth-sig-util": "^3.0.0", + "ethereumjs-util": "^7.0.7", + "ethereumjs-wallet": "^1.0.1", + "glob": "^8.0.3", + "graphlib": "^2.1.8", + "hardhat": "^2.9.1", + "hardhat-gas-reporter": "^1.0.4", + "hardhat-ignore-warnings": "^0.2.0", + "keccak256": "^1.0.2", + "lodash.startcase": "^4.4.0", + "lodash.zip": "^4.2.0", + "merkletreejs": "^0.2.13", + "micromatch": "^4.0.2", + "prettier": "^2.3.0", + "prettier-plugin-solidity": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "solhint": "^3.3.6", + "solidity-ast": "^0.4.25", + "solidity-coverage": "^0.8.0", + "solidity-docgen": "^0.6.0-beta.29", + "web3": "^1.3.0", + "yargs": "^17.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ensdomains/address-encoder": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz", + "integrity": "sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg==", + "dev": true, + "dependencies": { + "bech32": "^1.1.3", + "blakejs": "^1.1.0", + "bn.js": "^4.11.8", + "bs58": "^4.0.1", + "crypto-addr-codec": "^0.1.7", + "nano-base32": "^1.0.1", + "ripemd160": "^2.0.2" + } + }, + "node_modules/@ensdomains/ens": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@ensdomains/ens/-/ens-0.4.5.tgz", + "integrity": "sha512-JSvpj1iNMFjK6K+uVl4unqMoa9rf5jopb8cya5UGBWz23Nw8hSNT7efgUx4BTlAPAgpNlEioUfeTyQ6J9ZvTVw==", + "deprecated": "Please use @ensdomains/ens-contracts", + "dev": true, + "dependencies": { + "bluebird": "^3.5.2", + "eth-ens-namehash": "^2.0.8", + "solc": "^0.4.20", + "testrpc": "0.0.1", + "web3-utils": "^1.0.0-beta.31" + } + }, + "node_modules/@ensdomains/ensjs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@ensdomains/ensjs/-/ensjs-2.1.0.tgz", + "integrity": "sha512-GRbGPT8Z/OJMDuxs75U/jUNEC0tbL0aj7/L/QQznGYKm/tiasp+ndLOaoULy9kKJFC0TBByqfFliEHDgoLhyog==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@ensdomains/address-encoder": "^0.1.7", + "@ensdomains/ens": "0.4.5", + "@ensdomains/resolver": "0.2.4", + "content-hash": "^2.5.2", + "eth-ens-namehash": "^2.0.8", + "ethers": "^5.0.13", + "js-sha3": "^0.8.0" + } + }, + "node_modules/@ensdomains/ensjs/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/@ensdomains/resolver": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ensdomains/resolver/-/resolver-0.2.4.tgz", + "integrity": "sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==", + "deprecated": "Please use @ensdomains/ens-contracts", + "dev": true + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@ethereumjs/common": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", + "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.1" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", + "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", + "dev": true, + "dependencies": { + "@ethereumjs/common": "^2.5.0", + "ethereumjs-util": "^7.1.2" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bignumber/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@frangio/servbot": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@frangio/servbot/-/servbot-0.2.5.tgz", + "integrity": "sha512-ogja4iAPZ1VwM5MU3C1ZhB88358F0PGbmSTGOkIZwOyLaDoMHIqOVCnavHjR7DV5h+oAI4Z4KDqlam3myQUrmg==", + "dev": true, + "engines": { + "node": ">=12.x", + "pnpm": "7.5.1" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/ethereumjs-block": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz", + "integrity": "sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-blockchain": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz", + "integrity": "sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-ethash": "^2.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "level": "^8.0.0", + "lru-cache": "^5.1.1", + "memory-level": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz", + "integrity": "sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@nomicfoundation/ethereumjs-ethash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz", + "integrity": "sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "bigint-crypto-utils": "^3.0.23", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-evm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz", + "integrity": "sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz", + "integrity": "sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==", + "dev": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-statemanager": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz", + "integrity": "sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1" + } + }, + "node_modules/@nomicfoundation/ethereumjs-trie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz", + "integrity": "sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz", + "integrity": "sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz", + "integrity": "sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0-beta.2", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-vm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz", + "integrity": "sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==", + "dev": true, + "dependencies": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.7.tgz", + "integrity": "sha512-X+3mNvn8B7BY5hpIaLO+TrfzWq12bpux+ajGGdmdcfC78NXmYmOZkAtiz1QZx1YIZGMS1LaXzPXyBExxKFpCaw==", + "dev": true, + "dependencies": { + "ethereumjs-util": "^7.1.4" + }, + "peerDependencies": { + "hardhat": "^2.9.5" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz", + "integrity": "sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg==", + "dev": true, + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.0", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.0", + "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.0", + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.0", + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.0", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.0" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz", + "integrity": "sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.0.tgz", + "integrity": "sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-freebsd-x64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.0.tgz", + "integrity": "sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.0.tgz", + "integrity": "sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.0.tgz", + "integrity": "sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.0.tgz", + "integrity": "sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.0.tgz", + "integrity": "sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.0.tgz", + "integrity": "sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.0.tgz", + "integrity": "sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.0.tgz", + "integrity": "sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomiclabs/hardhat-truffle5": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-truffle5/-/hardhat-truffle5-2.0.7.tgz", + "integrity": "sha512-Pw8451IUZp1bTp0QqCHCYfCHs66sCnyxPcaorapu9mfOV9xnZsVaFdtutnhNEiXdiZwbed7LFKpRsde4BjFwig==", + "dev": true, + "dependencies": { + "@nomiclabs/truffle-contract": "^4.2.23", + "@types/chai": "^4.2.0", + "chai": "^4.2.0", + "ethereumjs-util": "^7.1.4", + "fs-extra": "^7.0.1" + }, + "peerDependencies": { + "@nomiclabs/hardhat-web3": "^2.0.0", + "hardhat": "^2.6.4", + "web3": "^1.0.0-beta.36" + } + }, + "node_modules/@nomiclabs/hardhat-web3": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-web3/-/hardhat-web3-2.0.0.tgz", + "integrity": "sha512-zt4xN+D+fKl3wW2YlTX3k9APR3XZgPkxJYf36AcliJn3oujnKEVRZaHu0PhgLjO+gR+F/kiYayo9fgd2L8970Q==", + "dev": true, + "dependencies": { + "@types/bignumber.js": "^5.0.0" + }, + "peerDependencies": { + "hardhat": "^2.0.0", + "web3": "^1.0.0-beta.36" + } + }, + "node_modules/@nomiclabs/truffle-contract": { + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@nomiclabs/truffle-contract/-/truffle-contract-4.5.10.tgz", + "integrity": "sha512-nF/6InFV+0hUvutyFgsdOMCoYlr//2fJbRER4itxYtQtc4/O1biTwZIKRu+5l2J5Sq6LU2WX7vZHtDgQdhWxIQ==", + "dev": true, + "dependencies": { + "@ensdomains/ensjs": "^2.0.1", + "@truffle/blockchain-utils": "^0.1.3", + "@truffle/contract-schema": "^3.4.7", + "@truffle/debug-utils": "^6.0.22", + "@truffle/error": "^0.1.0", + "@truffle/interface-adapter": "^0.5.16", + "bignumber.js": "^7.2.1", + "ethereum-ens": "^0.8.0", + "ethers": "^4.0.0-beta.1", + "source-map-support": "^0.5.19" + }, + "peerDependencies": { + "web3": "^1.2.1", + "web3-core-helpers": "^1.2.1", + "web3-core-promievent": "^1.2.1", + "web3-eth-abi": "^1.2.1", + "web3-utils": "^1.2.1" + } + }, + "node_modules/@openzeppelin/contract-loader": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contract-loader/-/contract-loader-0.6.3.tgz", + "integrity": "sha512-cOFIjBjwbGgZhDZsitNgJl0Ye1rd5yu/Yx5LMgeq3u0ZYzldm4uObzHDFq4gjDdoypvyORjjJa3BlFA7eAnVIg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + } + }, + "node_modules/@openzeppelin/contract-loader/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@openzeppelin/docs-utils": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/docs-utils/-/docs-utils-0.1.3.tgz", + "integrity": "sha512-O/iJ4jEi5ryNc/T74G9gbnFwQ8QaQ2bpAVoYXLPknZJyK52GEAvxC12UMP33KodTNV3rMzeeQrSBIdI8skjDJg==", + "dev": true, + "dependencies": { + "@frangio/servbot": "^0.2.5", + "chalk": "^3.0.0", + "chokidar": "^3.5.3", + "env-paths": "^2.2.0", + "find-up": "^4.1.0", + "is-port-reachable": "^3.0.0", + "js-yaml": "^3.13.1", + "lodash.startcase": "^4.4.0", + "minimist": "^1.2.0" + }, + "bin": { + "oz-docs": "oz-docs.js" + } + }, + "node_modules/@openzeppelin/test-helpers": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@openzeppelin/test-helpers/-/test-helpers-0.5.16.tgz", + "integrity": "sha512-T1EvspSfH1qQO/sgGlskLfYVBbqzJR23SZzYl/6B2JnT4EhThcI85UpvDk0BkLWKaDScQTabGHt4GzHW+3SfZg==", + "dev": true, + "dependencies": { + "@openzeppelin/contract-loader": "^0.6.2", + "@truffle/contract": "^4.0.35", + "ansi-colors": "^3.2.3", + "chai": "^4.2.0", + "chai-bn": "^0.2.1", + "ethjs-abi": "^0.2.1", + "lodash.flatten": "^4.4.0", + "semver": "^5.6.0", + "web3": "^1.2.5", + "web3-utils": "^1.2.5" + } + }, + "node_modules/@openzeppelin/test-helpers/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@scure/bip32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", + "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@noble/secp256k1": "~1.6.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@truffle/abi-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.3.6.tgz", + "integrity": "sha512-61aTH2QmwVA1INaPMufRHTsS6jsEhS+GCkuCDdvBDmwctSnCKGDOr185BGt65QrpMRxYmIoH6WFBSNMYxW9GRw==", + "dev": true, + "dependencies": { + "change-case": "3.0.2", + "fast-check": "3.1.1", + "web3-utils": "1.8.1" + } + }, + "node_modules/@truffle/blockchain-utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@truffle/blockchain-utils/-/blockchain-utils-0.1.6.tgz", + "integrity": "sha512-SldoNRIFSm3+HMBnSc2jFsu5TWDkCN4X6vL3wrd0t6DIeF7nD6EoPPjxwbFSoqCnkkRxMuZeL6sUx7UMJS/wSA==", + "dev": true + }, + "node_modules/@truffle/codec": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.14.11.tgz", + "integrity": "sha512-NgfMNYemgMXqoEcJA5ZsEhxChCwq33rSxtNxlececEH/1Nf0r+ryfrfmLlyPmv8f3jorVf1GWa0zI0AedGCGYQ==", + "dev": true, + "dependencies": { + "@truffle/abi-utils": "^0.3.6", + "@truffle/compile-common": "^0.9.1", + "big.js": "^6.0.3", + "bn.js": "^5.1.3", + "cbor": "^5.2.0", + "debug": "^4.3.1", + "lodash": "^4.17.21", + "semver": "7.3.7", + "utf8": "^3.0.0", + "web3-utils": "1.8.1" + } + }, + "node_modules/@truffle/codec/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/@truffle/codec/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@truffle/codec/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@truffle/codec/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@truffle/compile-common": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.9.1.tgz", + "integrity": "sha512-mhdkX6ExZImHSBO3jGm6aAn8NpVtMTdjq50jRXY/O59/ZNC0J9WpRapxrAKUVNc+XydMdBlfeEpXoqTJg7cbXw==", + "dev": true, + "dependencies": { + "@truffle/error": "^0.1.1", + "colors": "1.4.0" + } + }, + "node_modules/@truffle/contract": { + "version": "4.6.10", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.6.10.tgz", + "integrity": "sha512-69IZSXeQKRP3EutILqe+vLY5A5gUpeXUiZhm/Fy/qHHkP238vMjtOkTZGkY6bonYqmgk+vDY7KSYSYKzDNPdCA==", + "dev": true, + "dependencies": { + "@ensdomains/ensjs": "^2.1.0", + "@truffle/blockchain-utils": "^0.1.6", + "@truffle/contract-schema": "^3.4.11", + "@truffle/debug-utils": "^6.0.42", + "@truffle/error": "^0.1.1", + "@truffle/interface-adapter": "^0.5.26", + "bignumber.js": "^7.2.1", + "debug": "^4.3.1", + "ethers": "^4.0.32", + "web3": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "node_modules/@truffle/contract-schema": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.11.tgz", + "integrity": "sha512-wReyVZUPyU9Zy5PSCugBLG1nnruBmRAJ/gmoirQiJ9N2n+s1iGBTY49tkDqFMz3XUUE0kplfdb9YKZJlLkTWzQ==", + "dev": true, + "dependencies": { + "ajv": "^6.10.0", + "debug": "^4.3.1" + } + }, + "node_modules/@truffle/debug-utils": { + "version": "6.0.42", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.42.tgz", + "integrity": "sha512-9v70tj+My0Z2UZJ9OsuUlfo4Dt2AJqAQa/YWtGe28H8zsi+o9Dca0RsKWecuprdllgzrEs7ad8QUtSINhwjIlg==", + "dev": true, + "dependencies": { + "@truffle/codec": "^0.14.11", + "@trufflesuite/chromafi": "^3.0.0", + "bn.js": "^5.1.3", + "chalk": "^2.4.2", + "debug": "^4.3.1", + "highlightjs-solidity": "^2.0.5" + } + }, + "node_modules/@truffle/debug-utils/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@truffle/debug-utils/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/@truffle/debug-utils/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@truffle/debug-utils/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@truffle/debug-utils/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@truffle/debug-utils/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@truffle/debug-utils/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@truffle/debug-utils/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@truffle/error": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@truffle/error/-/error-0.1.1.tgz", + "integrity": "sha512-sE7c9IHIGdbK4YayH4BC8i8qMjoAOeg6nUXUDZZp8wlU21/EMpaG+CLx+KqcIPyR+GSWIW3Dm0PXkr2nlggFDA==", + "dev": true + }, + "node_modules/@truffle/interface-adapter": { + "version": "0.5.26", + "resolved": "https://registry.npmjs.org/@truffle/interface-adapter/-/interface-adapter-0.5.26.tgz", + "integrity": "sha512-fBhoqtT+CT4XKXcOijvw0RIMgyUi3FJg+n5i5PyGBsoRzqbLZd9cZq+oMNjOZPdf3GH68hsOFOaQO5tZH7oZow==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.3", + "ethers": "^4.0.32", + "web3": "1.8.1" + } + }, + "node_modules/@truffle/interface-adapter/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/@trufflesuite/chromafi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz", + "integrity": "sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0", + "chalk": "^2.3.2", + "cheerio": "^1.0.0-rc.2", + "detect-indent": "^5.0.0", + "highlight.js": "^10.4.1", + "lodash.merge": "^4.6.2", + "strip-ansi": "^4.0.0", + "strip-indent": "^2.0.0" + } + }, + "node_modules/@trufflesuite/chromafi/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@trufflesuite/chromafi/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@trufflesuite/chromafi/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@trufflesuite/chromafi/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@trufflesuite/chromafi/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@trufflesuite/chromafi/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@trufflesuite/chromafi/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@types/async-eventemitter": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz", + "integrity": "sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==", + "dev": true + }, + "node_modules/@types/bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA==", + "deprecated": "This is a stub types definition for bignumber.js (https://github.com/MikeMcl/bignumber.js/). bignumber.js provides its own type definitions, so you don't need @types/bignumber.js installed!", + "dev": true, + "dependencies": { + "bignumber.js": "*" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "dev": true + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", + "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "dev": true + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==", + "dev": true + }, + "node_modules/abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/abstract-level/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz", + "integrity": "sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ==", + "dev": true + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ast-parents": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", + "integrity": "sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==", + "dev": true + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "dev": true, + "dependencies": { + "async": "^2.4.0" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true + }, + "node_modules/big-integer": { + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/big.js": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", + "integrity": "sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bigint-crypto-utils": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.8.tgz", + "integrity": "sha512-+VMV9Laq8pXLBKKKK49nOoq9bfR3j7NNQAtbA617a4nw9bVLo8rsqkKMBgM2AJWlNX9fEIyYaYX+d0laqYV4tw==", + "dev": true, + "dependencies": { + "bigint-mod-arith": "^3.1.0" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/bigint-mod-arith": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz", + "integrity": "sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ==", + "dev": true, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "dev": true, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-rsa/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-reverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", + "integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==", + "dev": true + }, + "node_modules/buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "node_modules/bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cbor": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz", + "integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==", + "dev": true, + "dependencies": { + "bignumber.js": "^9.0.1", + "nofilter": "^1.0.4" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cbor/node_modules/bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-bn": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/chai-bn/-/chai-bn-0.2.2.tgz", + "integrity": "sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg==", + "dev": true, + "peerDependencies": { + "bn.js": "^4.11.0", + "chai": "^4.0.0" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/change-case": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.2.tgz", + "integrity": "sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==", + "dev": true, + "dependencies": { + "camel-case": "^3.0.0", + "constant-case": "^2.0.0", + "dot-case": "^2.1.0", + "header-case": "^1.0.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "no-case": "^2.3.2", + "param-case": "^2.1.0", + "pascal-case": "^2.0.0", + "path-case": "^2.1.0", + "sentence-case": "^2.1.0", + "snake-case": "^2.1.0", + "swap-case": "^1.1.0", + "title-case": "^2.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/cids/node_modules/multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==", + "dev": true + }, + "node_modules/classic-level": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz", + "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true + }, + "node_modules/commander": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/constant-case": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", + "integrity": "sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==", + "dev": true, + "dependencies": { + "snake-case": "^2.1.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "dev": true, + "dependencies": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "dependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/crypto-addr-codec": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", + "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", + "dev": true, + "dependencies": { + "base-x": "^3.0.8", + "big-integer": "1.6.36", + "blakejs": "^1.1.0", + "bs58": "^4.0.1", + "ripemd160-min": "0.0.6", + "safe-buffer": "^5.2.0", + "sha3": "^2.1.1" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/crypto-js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==", + "dev": true + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "dev": true, + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "dependencies": { + "heap": ">= 0.2.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", + "integrity": "sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", + "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/enquirer/node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", + "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "unbox-primitive": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": "^7.12.1", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1 || ^5.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/eslint-plugin-mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz", + "integrity": "sha512-xLqqWUF17llsogVOC+8C6/jvQ+4IoOREbN7ZCHuOHuD6cT5cDD4h7f2LgsZuzMAiwswWE21tO7ExaknHVDrSkw==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "rambda": "^7.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-mocha/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", + "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", + "dev": true, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", + "dev": true, + "dependencies": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + } + }, + "node_modules/eth-ens-namehash/node_modules/js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", + "dev": true + }, + "node_modules/eth-gas-reporter": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz", + "integrity": "sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.0.0-beta.146", + "@solidity-parser/parser": "^0.14.0", + "cli-table3": "^0.5.0", + "colors": "1.4.0", + "ethereum-cryptography": "^1.0.3", + "ethers": "^4.0.40", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^7.1.1", + "req-cwd": "^2.0.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "peerDependencies": { + "@codechecks/client": "^0.1.0" + }, + "peerDependenciesMeta": { + "@codechecks/client": { + "optional": true + } + } + }, + "node_modules/eth-gas-reporter/node_modules/ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eth-gas-reporter/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eth-gas-reporter/node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eth-gas-reporter/node_modules/chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/eth-gas-reporter/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/eth-gas-reporter/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/eth-gas-reporter/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/eth-gas-reporter/node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eth-gas-reporter/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eth-gas-reporter/node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eth-gas-reporter/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eth-gas-reporter/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eth-gas-reporter/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/eth-gas-reporter/node_modules/mocha": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", + "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", + "dev": true, + "dependencies": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/eth-gas-reporter/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/eth-gas-reporter/node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eth-gas-reporter/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eth-gas-reporter/node_modules/readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eth-gas-reporter/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/eth-gas-reporter/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-gas-reporter/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/eth-gas-reporter/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/eth-gas-reporter/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/eth-lib/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/eth-lib/node_modules/ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "node_modules/eth-sig-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-3.0.1.tgz", + "integrity": "sha512-0Us50HiGGvZgjtWTyAI/+qTzYPMLy5Q451D0Xy68bxq1QMWdoOddDwGvsqcFT27uohKgalM9z/yxplyt+mY2iQ==", + "deprecated": "Deprecated in favor of '@metamask/eth-sig-util'", + "dev": true, + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^5.1.1", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.0" + } + }, + "node_modules/eth-sig-util/node_modules/ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "dev": true, + "dependencies": { + "js-sha3": "^0.8.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereum-ens": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/ethereum-ens/-/ethereum-ens-0.8.0.tgz", + "integrity": "sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg==", + "dev": true, + "dependencies": { + "bluebird": "^3.4.7", + "eth-ens-namehash": "^2.0.0", + "js-sha3": "^0.5.7", + "pako": "^1.0.4", + "underscore": "^1.8.3", + "web3": "^1.0.0-beta.34" + } + }, + "node_modules/ethereum-ens/node_modules/js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", + "dev": true + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/ethereumjs-wallet": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz", + "integrity": "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA==", + "dev": true, + "dependencies": { + "aes-js": "^3.1.2", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^7.1.2", + "randombytes": "^2.1.0", + "scrypt-js": "^3.0.1", + "utf8": "^3.0.0", + "uuid": "^8.3.2" + } + }, + "node_modules/ethers": { + "version": "4.0.49", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz", + "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==", + "dev": true, + "dependencies": { + "aes-js": "3.0.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "node_modules/ethers/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true + }, + "node_modules/ethers/node_modules/hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/ethers/node_modules/js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", + "dev": true + }, + "node_modules/ethers/node_modules/scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==", + "dev": true + }, + "node_modules/ethers/node_modules/setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==", + "dev": true + }, + "node_modules/ethers/node_modules/uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true + }, + "node_modules/ethjs-abi": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.1.tgz", + "integrity": "sha512-g2AULSDYI6nEJyJaEVEXtTimRY2aPC2fi7ddSy0W+LXvEVL8Fe1y76o43ecbgdUKwZD+xsmEgX1yJr1Ia3r1IA==", + "dev": true, + "dependencies": { + "bn.js": "4.11.6", + "js-sha3": "0.5.5", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-abi/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true + }, + "node_modules/ethjs-abi/node_modules/js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha512-yLLwn44IVeunwjpDVTDZmQeVbB0h+dZpY2eO68B/Zik8hu6dH+rKeLxwua79GGIvW6xr8NBAcrtiUbYrTjEFTA==", + "dev": true + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "dev": true + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-check": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.1.1.tgz", + "integrity": "sha512-3vtXinVyuUKCKFKYcwXhGE6NtGWkqF8Yh3rvMZNzmwz8EPrgoc/v4pDdLHyLnCyCI5MZpZZkDEwFyXyEONOxpA==", + "dev": true, + "dependencies": { + "pure-rand": "^5.0.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", + "dev": true + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/ghost-testrpc/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ghost-testrpc/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/ghost-testrpc/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ghost-testrpc/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ghost-testrpc/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/globby/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/hardhat": { + "version": "2.12.4", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.12.4.tgz", + "integrity": "sha512-rc9S2U/4M+77LxW1Kg7oqMMmjl81tzn5rNFARhbXKUA1am/nhfMJEujOjuKvt+ZGMiZ11PYSe8gyIpB/aRNDgw==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@nomicfoundation/ethereumjs-vm": "^6.0.0", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "abort-controller": "^3.0.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "chalk": "^2.4.2", + "chokidar": "^3.4.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "qs": "^6.7.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.7.3", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.4.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/cli.js" + }, + "engines": { + "node": "^14.0.0 || ^16.0.0 || ^18.0.0" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-gas-reporter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", + "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==", + "dev": true, + "dependencies": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + }, + "peerDependencies": { + "hardhat": "^2.0.2" + } + }, + "node_modules/hardhat-ignore-warnings": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.2.6.tgz", + "integrity": "sha512-GQgvjprONI8VF8b85+QJ8H9v3L9TCCtQvUx+9QaRL+sCPw1cOZHfhlEz9V6Lq7GNCQMqBORVzNzUzoP/tKAEQQ==", + "dev": true, + "dependencies": { + "minimatch": "^5.1.0", + "node-interval-tree": "^2.0.1", + "solidity-comments": "^0.0.2" + } + }, + "node_modules/hardhat-ignore-warnings/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/hardhat-ignore-warnings/node_modules/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hardhat/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/hardhat/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/hardhat/node_modules/commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "dev": true + }, + "node_modules/hardhat/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" + } + }, + "node_modules/hardhat/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/hardhat/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/hardhat/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hardhat/node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hardhat/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/hardhat/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/hardhat/node_modules/solc": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", + "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", + "dev": true, + "dependencies": { + "command-exists": "^1.2.8", + "commander": "3.0.2", + "follow-redirects": "^1.12.1", + "fs-extra": "^0.30.0", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solcjs" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/hardhat/node_modules/solc/node_modules/fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "node_modules/hardhat/node_modules/solc/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/hardhat/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/header-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", + "integrity": "sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.3" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-solidity": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.5.tgz", + "integrity": "sha512-ReXxQSGQkODMUgHcWzVSnfDCDrL2HshOYgw3OlIYmfHeRzUPkfJTUIp95pK4CmbiNG2eMTOmNLpfCz9Zq7Cwmg==", + "dev": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==", + "dev": true + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "dev": true, + "dependencies": { + "punycode": "2.1.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz", + "integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/inquirer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-port-reachable": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-3.1.0.tgz", + "integrity": "sha512-vjc0SSRNZ32s9SbZBzGaiP6YVB+xglLShhgZD/FHMZUXBvQWaV9CtzgeVhjccFJrI6RAMV+LX7NYxueW/A8W5A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==", + "dev": true, + "dependencies": { + "upper-case": "^1.1.0" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keccak256": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz", + "integrity": "sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.0", + "buffer": "^6.0.3", + "keccak": "^3.0.2" + } + }, + "node_modules/keccak256/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/keccak256/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/level": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", + "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", + "dev": true, + "dependencies": { + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/level" + } + }, + "node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/level-transcoder/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "dev": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true + }, + "node_modules/lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.2" + } + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true + }, + "node_modules/mcl-wasm": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", + "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", + "dev": true, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-level": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", + "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", + "dev": true, + "dependencies": { + "abstract-level": "^1.0.0", + "functional-red-black-tree": "^1.0.1", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/merkletreejs": { + "version": "0.2.32", + "resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.32.tgz", + "integrity": "sha512-TostQBiwYRIwSE5++jGmacu3ODcKAgqb0Y/pnIohXS7sWxh1gCkSptbmF1a43faehRDpcHf7J/kv0Ml2D/zblQ==", + "dev": true, + "dependencies": { + "bignumber.js": "^9.0.1", + "buffer-reverse": "^1.0.1", + "crypto-js": "^3.1.9-1", + "treeify": "^1.1.0", + "web3-utils": "^1.3.4" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/merkletreejs/node_modules/bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "dev": true, + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", + "deprecated": "This package is broken and no longer maintained. 'mkdirp' itself supports promises now, please switch to that.", + "dev": true, + "dependencies": { + "mkdirp": "*" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==", + "dev": true + }, + "node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "varint": "^5.0.0" + } + }, + "node_modules/multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multihashes/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true + }, + "node_modules/nano-base32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nano-base32/-/nano-base32-1.0.1.tgz", + "integrity": "sha512-sxEtoTqAPdjWVGv71Q17koMFGsOMSiHsIFEvzOM7cNp8BXB4AnEwmDabm5dorusJf/v1z7QxaZYxUorU9RKaAw==", + "dev": true + }, + "node_modules/nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-interval-tree": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-interval-tree/-/node-interval-tree-2.1.2.tgz", + "integrity": "sha512-bJ9zMDuNGzVQg1xv0bCPzyEDxHgbrx7/xGj6CDokvizZZmastPsOh0JJLuY8wA5q2SfX1TLNMk7XNV8WxbGxzA==", + "dev": true, + "dependencies": { + "shallowequal": "^1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nofilter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz", + "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dev": true, + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true + }, + "node_modules/oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", + "dev": true, + "dependencies": { + "http-https": "^1.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", + "integrity": "sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==", + "dev": true, + "dependencies": { + "camel-case": "^3.0.0", + "upper-case-first": "^1.1.0" + } + }, + "node_modules/path-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", + "integrity": "sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", + "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-solidity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.0.tgz", + "integrity": "sha512-5gq0T49ifvXH/6x1STuKyWjTUgi6ICoV65yNtKlg/vZEvocFtSpByJOJICBfqPwNsnv4vhhWIqkLGSUJmWum2w==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.14.5", + "emoji-regex": "^10.2.1", + "escape-string-regexp": "^4.0.0", + "semver": "^7.3.8", + "solidity-comments-extractor": "^0.0.7", + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "prettier": "^2.3.0" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/prettier-plugin-solidity/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.5.tgz", + "integrity": "sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rambda": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.4.0.tgz", + "integrity": "sha512-A9hihu7dUTLOUCM+I8E61V4kRXnN4DwYeK0DwCBydC1MqNI1PidyAtbtpsJlBBzK4icSctEcCQ1bGcLpBuETUQ==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "dependencies": { + "req-from": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "request": "^2.34" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/responselike/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/rlp/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==", + "dev": true + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/sc-istanbul/node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true + }, + "node_modules/sc-istanbul/node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "node_modules/sc-istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sc-istanbul/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/sentence-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", + "integrity": "sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case-first": "^1.1.2" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "dev": true, + "dependencies": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "dependencies": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sha3": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "dev": true, + "dependencies": { + "buffer": "6.0.3" + } + }, + "node_modules/sha3/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", + "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", + "dev": true, + "dependencies": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/snake-case": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "integrity": "sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/solc": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz", + "integrity": "sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA==", + "dev": true, + "dependencies": { + "fs-extra": "^0.30.0", + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" + }, + "bin": { + "solcjs": "solcjs" + } + }, + "node_modules/solc/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/solc/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "node_modules/solc/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/solc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/solc/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solc/node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, + "node_modules/solc/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solc/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, + "node_modules/solc/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solc/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/solc/node_modules/yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha512-LqodLrnIDM3IFT+Hf/5sxBnEGECrfdC1uIbgZeJmESCSo4HoCAaKEus8MylXHAkdacGc0ye+Qa+dpkuom8uVYA==", + "dev": true, + "dependencies": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "node_modules/solc/node_modules/yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + }, + "node_modules/solhint": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.3.7.tgz", + "integrity": "sha512-NjjjVmXI3ehKkb3aNtRJWw55SUVJ8HMKKodwe0HnejA+k0d2kmhw7jvpa+MCTbcEgt8IWSwx0Hu6aCo/iYOZzQ==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.14.1", + "ajv": "^6.6.1", + "antlr4": "4.7.1", + "ast-parents": "0.0.1", + "chalk": "^2.4.2", + "commander": "2.18.0", + "cosmiconfig": "^5.0.7", + "eslint": "^5.6.0", + "fast-diff": "^1.1.2", + "glob": "^7.1.3", + "ignore": "^4.0.6", + "js-yaml": "^3.12.0", + "lodash": "^4.17.11", + "semver": "^6.3.0" + }, + "bin": { + "solhint": "solhint.js" + }, + "optionalDependencies": { + "prettier": "^1.14.3" + } + }, + "node_modules/solhint/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/solhint/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/solhint/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/solhint/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/solhint/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/solhint/node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solhint/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/solhint/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/solhint/node_modules/eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + } + }, + "node_modules/solhint/node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/solhint/node_modules/eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solhint/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/eslint/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solhint/node_modules/espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "dependencies": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/solhint/node_modules/file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "dependencies": { + "flat-cache": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "dependencies": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "node_modules/solhint/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/solhint/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/solhint/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/solhint/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/solhint/node_modules/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true, + "engines": { + "node": ">=6.5.0" + } + }, + "node_modules/solhint/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/solhint/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/solhint/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solhint/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solhint/node_modules/slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solhint/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solhint/node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solhint/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solhint/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solhint/node_modules/table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "dependencies": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/solhint/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/solhint/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/solidity-ast": { + "version": "0.4.39", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.39.tgz", + "integrity": "sha512-91d4HMzV9x3ZG1fXRtAFFq2UjJrQXkyWdrmzXqBlueOSGB+v+0+iiLfZIPnTE0apndG2zm23qkZQJf8IbRrf7w==", + "dev": true + }, + "node_modules/solidity-comments": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments/-/solidity-comments-0.0.2.tgz", + "integrity": "sha512-G+aK6qtyUfkn1guS8uzqUeua1dURwPlcOjoTYW/TwmXAcE7z/1+oGCfZUdMSe4ZMKklNbVZNiG5ibnF8gkkFfw==", + "dev": true, + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "solidity-comments-darwin-arm64": "0.0.2", + "solidity-comments-darwin-x64": "0.0.2", + "solidity-comments-freebsd-x64": "0.0.2", + "solidity-comments-linux-arm64-gnu": "0.0.2", + "solidity-comments-linux-arm64-musl": "0.0.2", + "solidity-comments-linux-x64-gnu": "0.0.2", + "solidity-comments-linux-x64-musl": "0.0.2", + "solidity-comments-win32-arm64-msvc": "0.0.2", + "solidity-comments-win32-ia32-msvc": "0.0.2", + "solidity-comments-win32-x64-msvc": "0.0.2" + } + }, + "node_modules/solidity-comments-darwin-arm64": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-darwin-arm64/-/solidity-comments-darwin-arm64-0.0.2.tgz", + "integrity": "sha512-HidWkVLSh7v+Vu0CA7oI21GWP/ZY7ro8g8OmIxE8oTqyMwgMbE8F1yc58Sj682Hj199HCZsjmtn1BE4PCbLiGA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-darwin-x64": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-darwin-x64/-/solidity-comments-darwin-x64-0.0.2.tgz", + "integrity": "sha512-Zjs0Ruz6faBTPT6fBecUt6qh4CdloT8Bwoc0+qxRoTn9UhYscmbPQkUgQEbS0FQPysYqVzzxJB4h1Ofbf4wwtA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "dev": true + }, + "node_modules/solidity-comments-freebsd-x64": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-freebsd-x64/-/solidity-comments-freebsd-x64-0.0.2.tgz", + "integrity": "sha512-8Qe4mpjuAxFSwZJVk7B8gAoLCdbtS412bQzBwk63L8dmlHogvE39iT70aAk3RHUddAppT5RMBunlPUCFYJ3ZTw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-linux-arm64-gnu": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-arm64-gnu/-/solidity-comments-linux-arm64-gnu-0.0.2.tgz", + "integrity": "sha512-spkb0MZZnmrP+Wtq4UxP+nyPAVRe82idOjqndolcNR0S9Xvu4ebwq+LvF4HiUgjTDmeiqYiFZQ8T9KGdLSIoIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-linux-arm64-musl": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-arm64-musl/-/solidity-comments-linux-arm64-musl-0.0.2.tgz", + "integrity": "sha512-guCDbHArcjE+JDXYkxx5RZzY1YF6OnAKCo+sTC5fstyW/KGKaQJNPyBNWuwYsQiaEHpvhW1ha537IvlGek8GqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-linux-x64-gnu": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-x64-gnu/-/solidity-comments-linux-x64-gnu-0.0.2.tgz", + "integrity": "sha512-zIqLehBK/g7tvrFmQljrfZXfkEeLt2v6wbe+uFu6kH/qAHZa7ybt8Vc0wYcmjo2U0PeBm15d79ee3AkwbIjFdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-linux-x64-musl": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-x64-musl/-/solidity-comments-linux-x64-musl-0.0.2.tgz", + "integrity": "sha512-R9FeDloVlFGTaVkOlELDVC7+1Tjx5WBPI5L8r0AGOPHK3+jOcRh6sKYpI+VskSPDc3vOO46INkpDgUXrKydlIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-win32-arm64-msvc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-win32-arm64-msvc/-/solidity-comments-win32-arm64-msvc-0.0.2.tgz", + "integrity": "sha512-QnWJoCQcJj+rnutULOihN9bixOtYWDdF5Rfz9fpHejL1BtNjdLW1om55XNVHGAHPqBxV4aeQQ6OirKnp9zKsug==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-win32-ia32-msvc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-win32-ia32-msvc/-/solidity-comments-win32-ia32-msvc-0.0.2.tgz", + "integrity": "sha512-vUg4nADtm/NcOtlIymG23NWJUSuMsvX15nU7ynhGBsdKtt8xhdP3C/zA6vjDk8Jg+FXGQL6IHVQ++g/7rSQi0w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-comments-win32-x64-msvc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-win32-x64-msvc/-/solidity-comments-win32-x64-msvc-0.0.2.tgz", + "integrity": "sha512-36j+KUF4V/y0t3qatHm/LF5sCUCBx2UndxE1kq5bOzh/s+nQgatuyB+Pd5BfuPQHdWu2KaExYe20FlAa6NL7+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/solidity-coverage": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz", + "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.14.1", + "chalk": "^2.4.2", + "death": "^1.1.0", + "detect-port": "^1.3.0", + "difflib": "^0.2.4", + "fs-extra": "^8.1.0", + "ghost-testrpc": "^0.0.2", + "global-modules": "^2.0.0", + "globby": "^10.0.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.15", + "mocha": "7.1.2", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "bin": { + "solidity-coverage": "plugins/bin.js" + }, + "peerDependencies": { + "hardhat": "^2.11.0" + } + }, + "node_modules/solidity-coverage/node_modules/ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.1" + } + }, + "node_modules/solidity-coverage/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/solidity-coverage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/solidity-coverage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/solidity-coverage/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/solidity-coverage/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solidity-coverage/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/solidity-coverage/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/solidity-coverage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/solidity-coverage/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/solidity-coverage/node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/solidity-coverage/node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/solidity-coverage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/solidity-coverage/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/solidity-coverage/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/solidity-coverage/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/solidity-coverage/node_modules/mocha": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", + "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", + "dev": true, + "dependencies": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/solidity-coverage/node_modules/mocha/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/solidity-coverage/node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/solidity-coverage/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/solidity-coverage/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/solidity-coverage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/solidity-coverage/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/solidity-coverage/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-coverage/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/solidity-coverage/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/solidity-coverage/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/solidity-coverage/node_modules/yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/solidity-docgen": { + "version": "0.6.0-beta.32", + "resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.6.0-beta.32.tgz", + "integrity": "sha512-LGcosbgqxrqTo9winVq3+Xz1shS9k4p+RKwqPVPI0HtjfsKF9Mc5GzBzOUhce9uwzy0yJfhf832whfY1UKt4Aw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7", + "solidity-ast": "^0.4.38" + }, + "peerDependencies": { + "hardhat": "^2.8.0" + } + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "node_modules/swarm-js": { + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz", + "integrity": "sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^11.8.5", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + } + }, + "node_modules/swarm-js/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swarm-js/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/swarm-js/node_modules/fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "node_modules/swarm-js/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/swarm-js/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/swarm-js/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/swarm-js/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dev": true, + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/testrpc": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/testrpc/-/testrpc-0.0.1.tgz", + "integrity": "sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA==", + "deprecated": "testrpc has been renamed to ganache-cli, please use this package from now on.", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/title-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.0.3" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tough-cookie/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "node_modules/undici": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", + "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", + "dev": true, + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.18" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "dev": true + }, + "node_modules/upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", + "dev": true, + "dependencies": { + "upper-case": "^1.1.1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==", + "dev": true + }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/web3": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.8.1.tgz", + "integrity": "sha512-tAqFsQhGv340C9OgRJIuoScN7f7wa1tUvsnnDUMt9YE6J4gcm7TV2Uwv+KERnzvV+xgdeuULYpsioRRNKrUvoQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "web3-bzz": "1.8.1", + "web3-core": "1.8.1", + "web3-eth": "1.8.1", + "web3-eth-personal": "1.8.1", + "web3-net": "1.8.1", + "web3-shh": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-bzz": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.8.1.tgz", + "integrity": "sha512-dJJHS84nvpoxv6ijTMkdUSlRr5beCXNtx4UZcrFLHBva8dT63QEtKdLyDt2AyMJJdVzTCk78uir/6XtVWrdS6w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@types/node": "^12.12.6", + "got": "12.1.0", + "swarm-js": "^0.1.40" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-bzz/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "node_modules/web3-core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.8.1.tgz", + "integrity": "sha512-LbRZlJH2N6nS3n3Eo9Y++25IvzMY7WvYnp4NM/Ajhh97dAdglYs6rToQ2DbL2RLvTYmTew4O/y9WmOk4nq9COw==", + "dev": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-requestmanager": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-helpers": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.8.1.tgz", + "integrity": "sha512-ClzNO6T1S1gifC+BThw0+GTfcsjLEY8T1qUp6Ly2+w4PntAdNtKahxWKApWJ0l9idqot/fFIDXwO3Euu7I0Xqw==", + "dev": true, + "dependencies": { + "web3-eth-iban": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-method": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.8.1.tgz", + "integrity": "sha512-oYGRodktfs86NrnFwaWTbv2S38JnpPslFwSSARwFv4W9cjbGUW3LDeA5MKD/dRY+ssZ5OaekeMsUCLoGhX68yA==", + "dev": true, + "dependencies": { + "@ethersproject/transactions": "^5.6.2", + "web3-core-helpers": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-promievent": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.8.1.tgz", + "integrity": "sha512-9mxqHlgB0MrZI4oUIRFkuoJMNj3E7btjrMv3sMer/Z9rYR1PfoSc1aAokw4rxKIcAh+ylVtd/acaB2HKB7aRPg==", + "dev": true, + "dependencies": { + "eventemitter3": "4.0.4" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-requestmanager": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.8.1.tgz", + "integrity": "sha512-x+VC2YPPwZ1khvqA6TA69LvfFCOZXsoUVOxmTx/vIN22PrY9KzKhxcE7pBSiGhmab1jtmRYXUbcQSVpAXqL8cw==", + "dev": true, + "dependencies": { + "util": "^0.12.0", + "web3-core-helpers": "1.8.1", + "web3-providers-http": "1.8.1", + "web3-providers-ipc": "1.8.1", + "web3-providers-ws": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core-subscriptions": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.8.1.tgz", + "integrity": "sha512-bmCMq5OeA3E2vZUh8Js1HcJbhwtsE+yeMqGC4oIZB3XsL5SLqyKLB/pU+qUYqQ9o4GdcrFTDPhPg1bgvf7p1Pw==", + "dev": true, + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-core/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "node_modules/web3-core/node_modules/bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/web3-eth": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.8.1.tgz", + "integrity": "sha512-LgyzbhFqiFRd8M8sBXoFN4ztzOnkeckl3H/9lH5ek7AdoRMhBg7tYpYRP3E5qkhd/q+yiZmcUgy1AF6NHrC1wg==", + "dev": true, + "dependencies": { + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-eth-accounts": "1.8.1", + "web3-eth-contract": "1.8.1", + "web3-eth-ens": "1.8.1", + "web3-eth-iban": "1.8.1", + "web3-eth-personal": "1.8.1", + "web3-net": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-abi": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.8.1.tgz", + "integrity": "sha512-0mZvCRTIG0UhDhJwNQJgJxu4b4DyIpuMA0GTfqxqeuqzX4Q/ZvmoNurw0ExTfXaGPP82UUmmdkRi6FdZOx+C6w==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-accounts": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.8.1.tgz", + "integrity": "sha512-mgzxSYgN54/NsOFBO1Fq1KkXp1S5KlBvI/DlgvajU72rupoFMq6Cu6Yp9GUaZ/w2ij9PzEJuFJk174XwtfMCmg==", + "dev": true, + "dependencies": { + "@ethereumjs/common": "2.5.0", + "@ethereumjs/tx": "3.3.2", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "^9.0.0", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-accounts/node_modules/eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "node_modules/web3-eth-accounts/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/web3-eth-contract": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.8.1.tgz", + "integrity": "sha512-1wphnl+/xwCE2io44JKnN+ti3oa47BKRiVzvWd42icwRbcpFfRxH9QH+aQX3u8VZIISNH7dAkTWpGIIJgGFTmg==", + "dev": true, + "dependencies": { + "@types/bn.js": "^5.1.0", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-ens": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.8.1.tgz", + "integrity": "sha512-FT8xTI9uN8RxeBQa/W8pLa2aoFh4+EE34w7W2271LICKzla1dtLyb6XSdn48vsUcPmhWsTVk9mO9RTU0l4LGQQ==", + "dev": true, + "dependencies": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-eth-contract": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-iban": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.8.1.tgz", + "integrity": "sha512-DomoQBfvIdtM08RyMGkMVBOH0vpOIxSSQ+jukWk/EkMLGMWJtXw/K2c2uHAeq3L/VPWNB7zXV2DUEGV/lNE2Dg==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-iban/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/web3-eth-personal": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.8.1.tgz", + "integrity": "sha512-myIYMvj7SDIoV9vE5BkVdon3pya1WinaXItugoii2VoTcQNPOtBxmYVH+XS5ErzCJlnxzphpQrkywyY64bbbCA==", + "dev": true, + "dependencies": { + "@types/node": "^12.12.6", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-net": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-eth-personal/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "node_modules/web3-net": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.8.1.tgz", + "integrity": "sha512-LyEJAwogdFo0UAXZqoSJGFjopdt+kLw0P00FSZn2yszbgcoI7EwC+nXiOsEe12xz4LqpYLOtbR7+gxgiTVjjHQ==", + "dev": true, + "dependencies": { + "web3-core": "1.8.1", + "web3-core-method": "1.8.1", + "web3-utils": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-http": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.8.1.tgz", + "integrity": "sha512-1Zyts4O9W/UNEPkp+jyL19Jc3D15S4yp8xuLTjVhcUEAlHo24NDWEKxtZGUuHk4HrKL2gp8OlsDbJ7MM+ESDgg==", + "dev": true, + "dependencies": { + "abortcontroller-polyfill": "^1.7.3", + "cross-fetch": "^3.1.4", + "es6-promise": "^4.2.8", + "web3-core-helpers": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-ipc": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.8.1.tgz", + "integrity": "sha512-nw/W5nclvi+P2z2dYkLWReKLnocStflWqFl+qjtv0xn3MrUTyXMzSF0+61i77+16xFsTgzo4wS/NWIOVkR0EFA==", + "dev": true, + "dependencies": { + "oboe": "2.1.5", + "web3-core-helpers": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-providers-ws": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.8.1.tgz", + "integrity": "sha512-TNefIDAMpdx57+YdWpYZ/xdofS0P+FfKaDYXhn24ie/tH9G+AB+UBSOKnjN0KSadcRSCMBwGPRiEmNHPavZdsA==", + "dev": true, + "dependencies": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.1", + "websocket": "^1.0.32" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-shh": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.8.1.tgz", + "integrity": "sha512-sqHgarnfcY2Qt3PYS4R6YveHrDy7hmL09yeLLHHCI+RKirmjLVqV0rc5LJWUtlbYI+kDoa5gbgde489M9ZAC0g==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "web3-core": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-net": "1.8.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.1.tgz", + "integrity": "sha512-LgnM9p6V7rHHUGfpMZod+NST8cRfGzJ1BTXAyNo7A9cJX9LczBfSRxJp+U/GInYe9mby40t3v22AJdlELibnsQ==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereumjs-util": "^7.1.0", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dev": true, + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==", + "dev": true, + "bin": { + "window-size": "cli.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "dependencies": { + "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dev": true, + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "dev": true, + "dependencies": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "node_modules/xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "dev": true, + "dependencies": { + "xhr-request": "^1.1.0" + } + }, + "node_modules/xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "dev": true, + "engines": { + "node": ">=0.10.32" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@ensdomains/address-encoder": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz", + "integrity": "sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg==", + "dev": true, + "requires": { + "bech32": "^1.1.3", + "blakejs": "^1.1.0", + "bn.js": "^4.11.8", + "bs58": "^4.0.1", + "crypto-addr-codec": "^0.1.7", + "nano-base32": "^1.0.1", + "ripemd160": "^2.0.2" + } + }, + "@ensdomains/ens": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@ensdomains/ens/-/ens-0.4.5.tgz", + "integrity": "sha512-JSvpj1iNMFjK6K+uVl4unqMoa9rf5jopb8cya5UGBWz23Nw8hSNT7efgUx4BTlAPAgpNlEioUfeTyQ6J9ZvTVw==", + "dev": true, + "requires": { + "bluebird": "^3.5.2", + "eth-ens-namehash": "^2.0.8", + "solc": "^0.4.20", + "testrpc": "0.0.1", + "web3-utils": "^1.0.0-beta.31" + } + }, + "@ensdomains/ensjs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@ensdomains/ensjs/-/ensjs-2.1.0.tgz", + "integrity": "sha512-GRbGPT8Z/OJMDuxs75U/jUNEC0tbL0aj7/L/QQznGYKm/tiasp+ndLOaoULy9kKJFC0TBByqfFliEHDgoLhyog==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@ensdomains/address-encoder": "^0.1.7", + "@ensdomains/ens": "0.4.5", + "@ensdomains/resolver": "0.2.4", + "content-hash": "^2.5.2", + "eth-ens-namehash": "^2.0.8", + "ethers": "^5.0.13", + "js-sha3": "^0.8.0" + }, + "dependencies": { + "ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "requires": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + } + } + }, + "@ensdomains/resolver": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ensdomains/resolver/-/resolver-0.2.4.tgz", + "integrity": "sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@ethereumjs/common": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.5.0.tgz", + "integrity": "sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.1" + } + }, + "@ethereumjs/tx": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.3.2.tgz", + "integrity": "sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog==", + "dev": true, + "requires": { + "@ethereumjs/common": "^2.5.0", + "ethereumjs-util": "^7.1.2" + } + }, + "@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "requires": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + }, + "dependencies": { + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true + } + } + }, + "@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true + }, + "@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + }, + "dependencies": { + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "requires": {} + } + } + }, + "@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "requires": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "requires": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "requires": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "@frangio/servbot": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@frangio/servbot/-/servbot-0.2.5.tgz", + "integrity": "sha512-ogja4iAPZ1VwM5MU3C1ZhB88358F0PGbmSTGOkIZwOyLaDoMHIqOVCnavHjR7DV5h+oAI4Z4KDqlam3myQUrmg==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "requires": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", + "dev": true + }, + "@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@nomicfoundation/ethereumjs-block": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz", + "integrity": "sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-blockchain": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz", + "integrity": "sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-ethash": "^2.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "level": "^8.0.0", + "lru-cache": "^5.1.1", + "memory-level": "^1.0.0" + } + }, + "@nomicfoundation/ethereumjs-common": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz", + "integrity": "sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "crc-32": "^1.2.0" + } + }, + "@nomicfoundation/ethereumjs-ethash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz", + "integrity": "sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "abstract-level": "^1.0.3", + "bigint-crypto-utils": "^3.0.23", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-evm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz", + "integrity": "sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + } + }, + "@nomicfoundation/ethereumjs-rlp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz", + "integrity": "sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==", + "dev": true + }, + "@nomicfoundation/ethereumjs-statemanager": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz", + "integrity": "sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1" + } + }, + "@nomicfoundation/ethereumjs-trie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz", + "integrity": "sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3", + "readable-stream": "^3.6.0" + } + }, + "@nomicfoundation/ethereumjs-tx": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz", + "integrity": "sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-util": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz", + "integrity": "sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-rlp": "^4.0.0-beta.2", + "ethereum-cryptography": "0.1.3" + } + }, + "@nomicfoundation/ethereumjs-vm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz", + "integrity": "sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==", + "dev": true, + "requires": { + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@types/async-eventemitter": "^0.2.1", + "async-eventemitter": "^0.2.4", + "debug": "^4.3.3", + "ethereum-cryptography": "0.1.3", + "functional-red-black-tree": "^1.0.1", + "mcl-wasm": "^0.7.1", + "rustbn.js": "~0.2.0" + } + }, + "@nomicfoundation/hardhat-network-helpers": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.7.tgz", + "integrity": "sha512-X+3mNvn8B7BY5hpIaLO+TrfzWq12bpux+ajGGdmdcfC78NXmYmOZkAtiz1QZx1YIZGMS1LaXzPXyBExxKFpCaw==", + "dev": true, + "requires": { + "ethereumjs-util": "^7.1.4" + } + }, + "@nomicfoundation/solidity-analyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz", + "integrity": "sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg==", + "dev": true, + "requires": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.0", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.0", + "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.0", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.0", + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.0", + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.0", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.0" + } + }, + "@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz", + "integrity": "sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.0.tgz", + "integrity": "sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-freebsd-x64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.0.tgz", + "integrity": "sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.0.tgz", + "integrity": "sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.0.tgz", + "integrity": "sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.0.tgz", + "integrity": "sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.0.tgz", + "integrity": "sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.0.tgz", + "integrity": "sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.0.tgz", + "integrity": "sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw==", + "dev": true, + "optional": true + }, + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.0.tgz", + "integrity": "sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA==", + "dev": true, + "optional": true + }, + "@nomiclabs/hardhat-truffle5": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-truffle5/-/hardhat-truffle5-2.0.7.tgz", + "integrity": "sha512-Pw8451IUZp1bTp0QqCHCYfCHs66sCnyxPcaorapu9mfOV9xnZsVaFdtutnhNEiXdiZwbed7LFKpRsde4BjFwig==", + "dev": true, + "requires": { + "@nomiclabs/truffle-contract": "^4.2.23", + "@types/chai": "^4.2.0", + "chai": "^4.2.0", + "ethereumjs-util": "^7.1.4", + "fs-extra": "^7.0.1" + } + }, + "@nomiclabs/hardhat-web3": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-web3/-/hardhat-web3-2.0.0.tgz", + "integrity": "sha512-zt4xN+D+fKl3wW2YlTX3k9APR3XZgPkxJYf36AcliJn3oujnKEVRZaHu0PhgLjO+gR+F/kiYayo9fgd2L8970Q==", + "dev": true, + "requires": { + "@types/bignumber.js": "^5.0.0" + } + }, + "@nomiclabs/truffle-contract": { + "version": "4.5.10", + "resolved": "https://registry.npmjs.org/@nomiclabs/truffle-contract/-/truffle-contract-4.5.10.tgz", + "integrity": "sha512-nF/6InFV+0hUvutyFgsdOMCoYlr//2fJbRER4itxYtQtc4/O1biTwZIKRu+5l2J5Sq6LU2WX7vZHtDgQdhWxIQ==", + "dev": true, + "requires": { + "@ensdomains/ensjs": "^2.0.1", + "@truffle/blockchain-utils": "^0.1.3", + "@truffle/contract-schema": "^3.4.7", + "@truffle/debug-utils": "^6.0.22", + "@truffle/error": "^0.1.0", + "@truffle/interface-adapter": "^0.5.16", + "bignumber.js": "^7.2.1", + "ethereum-ens": "^0.8.0", + "ethers": "^4.0.0-beta.1", + "source-map-support": "^0.5.19" + } + }, + "@openzeppelin/contract-loader": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contract-loader/-/contract-loader-0.6.3.tgz", + "integrity": "sha512-cOFIjBjwbGgZhDZsitNgJl0Ye1rd5yu/Yx5LMgeq3u0ZYzldm4uObzHDFq4gjDdoypvyORjjJa3BlFA7eAnVIg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "@openzeppelin/docs-utils": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/docs-utils/-/docs-utils-0.1.3.tgz", + "integrity": "sha512-O/iJ4jEi5ryNc/T74G9gbnFwQ8QaQ2bpAVoYXLPknZJyK52GEAvxC12UMP33KodTNV3rMzeeQrSBIdI8skjDJg==", + "dev": true, + "requires": { + "@frangio/servbot": "^0.2.5", + "chalk": "^3.0.0", + "chokidar": "^3.5.3", + "env-paths": "^2.2.0", + "find-up": "^4.1.0", + "is-port-reachable": "^3.0.0", + "js-yaml": "^3.13.1", + "lodash.startcase": "^4.4.0", + "minimist": "^1.2.0" + } + }, + "@openzeppelin/test-helpers": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@openzeppelin/test-helpers/-/test-helpers-0.5.16.tgz", + "integrity": "sha512-T1EvspSfH1qQO/sgGlskLfYVBbqzJR23SZzYl/6B2JnT4EhThcI85UpvDk0BkLWKaDScQTabGHt4GzHW+3SfZg==", + "dev": true, + "requires": { + "@openzeppelin/contract-loader": "^0.6.2", + "@truffle/contract": "^4.0.35", + "ansi-colors": "^3.2.3", + "chai": "^4.2.0", + "chai-bn": "^0.2.1", + "ethjs-abi": "^0.2.1", + "lodash.flatten": "^4.4.0", + "semver": "^5.6.0", + "web3": "^1.2.5", + "web3-utils": "^1.2.5" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "dev": true + }, + "@scure/bip32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", + "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", + "dev": true, + "requires": { + "@noble/hashes": "~1.1.1", + "@noble/secp256k1": "~1.6.0", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "dev": true, + "requires": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } + }, + "@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "requires": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "requires": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "requires": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "requires": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + } + }, + "@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "requires": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true + }, + "@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "requires": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true + }, + "@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.1" + } + }, + "@truffle/abi-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.3.6.tgz", + "integrity": "sha512-61aTH2QmwVA1INaPMufRHTsS6jsEhS+GCkuCDdvBDmwctSnCKGDOr185BGt65QrpMRxYmIoH6WFBSNMYxW9GRw==", + "dev": true, + "requires": { + "change-case": "3.0.2", + "fast-check": "3.1.1", + "web3-utils": "1.8.1" + } + }, + "@truffle/blockchain-utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@truffle/blockchain-utils/-/blockchain-utils-0.1.6.tgz", + "integrity": "sha512-SldoNRIFSm3+HMBnSc2jFsu5TWDkCN4X6vL3wrd0t6DIeF7nD6EoPPjxwbFSoqCnkkRxMuZeL6sUx7UMJS/wSA==", + "dev": true + }, + "@truffle/codec": { + "version": "0.14.11", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.14.11.tgz", + "integrity": "sha512-NgfMNYemgMXqoEcJA5ZsEhxChCwq33rSxtNxlececEH/1Nf0r+ryfrfmLlyPmv8f3jorVf1GWa0zI0AedGCGYQ==", + "dev": true, + "requires": { + "@truffle/abi-utils": "^0.3.6", + "@truffle/compile-common": "^0.9.1", + "big.js": "^6.0.3", + "bn.js": "^5.1.3", + "cbor": "^5.2.0", + "debug": "^4.3.1", + "lodash": "^4.17.21", + "semver": "7.3.7", + "utf8": "^3.0.0", + "web3-utils": "1.8.1" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@truffle/compile-common": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.9.1.tgz", + "integrity": "sha512-mhdkX6ExZImHSBO3jGm6aAn8NpVtMTdjq50jRXY/O59/ZNC0J9WpRapxrAKUVNc+XydMdBlfeEpXoqTJg7cbXw==", + "dev": true, + "requires": { + "@truffle/error": "^0.1.1", + "colors": "1.4.0" + } + }, + "@truffle/contract": { + "version": "4.6.10", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.6.10.tgz", + "integrity": "sha512-69IZSXeQKRP3EutILqe+vLY5A5gUpeXUiZhm/Fy/qHHkP238vMjtOkTZGkY6bonYqmgk+vDY7KSYSYKzDNPdCA==", + "dev": true, + "requires": { + "@ensdomains/ensjs": "^2.1.0", + "@truffle/blockchain-utils": "^0.1.6", + "@truffle/contract-schema": "^3.4.11", + "@truffle/debug-utils": "^6.0.42", + "@truffle/error": "^0.1.1", + "@truffle/interface-adapter": "^0.5.26", + "bignumber.js": "^7.2.1", + "debug": "^4.3.1", + "ethers": "^4.0.32", + "web3": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "@truffle/contract-schema": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.11.tgz", + "integrity": "sha512-wReyVZUPyU9Zy5PSCugBLG1nnruBmRAJ/gmoirQiJ9N2n+s1iGBTY49tkDqFMz3XUUE0kplfdb9YKZJlLkTWzQ==", + "dev": true, + "requires": { + "ajv": "^6.10.0", + "debug": "^4.3.1" + } + }, + "@truffle/debug-utils": { + "version": "6.0.42", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.42.tgz", + "integrity": "sha512-9v70tj+My0Z2UZJ9OsuUlfo4Dt2AJqAQa/YWtGe28H8zsi+o9Dca0RsKWecuprdllgzrEs7ad8QUtSINhwjIlg==", + "dev": true, + "requires": { + "@truffle/codec": "^0.14.11", + "@trufflesuite/chromafi": "^3.0.0", + "bn.js": "^5.1.3", + "chalk": "^2.4.2", + "debug": "^4.3.1", + "highlightjs-solidity": "^2.0.5" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@truffle/error": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@truffle/error/-/error-0.1.1.tgz", + "integrity": "sha512-sE7c9IHIGdbK4YayH4BC8i8qMjoAOeg6nUXUDZZp8wlU21/EMpaG+CLx+KqcIPyR+GSWIW3Dm0PXkr2nlggFDA==", + "dev": true + }, + "@truffle/interface-adapter": { + "version": "0.5.26", + "resolved": "https://registry.npmjs.org/@truffle/interface-adapter/-/interface-adapter-0.5.26.tgz", + "integrity": "sha512-fBhoqtT+CT4XKXcOijvw0RIMgyUi3FJg+n5i5PyGBsoRzqbLZd9cZq+oMNjOZPdf3GH68hsOFOaQO5tZH7oZow==", + "dev": true, + "requires": { + "bn.js": "^5.1.3", + "ethers": "^4.0.32", + "web3": "1.8.1" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "@trufflesuite/chromafi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-3.0.0.tgz", + "integrity": "sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "chalk": "^2.3.2", + "cheerio": "^1.0.0-rc.2", + "detect-indent": "^5.0.0", + "highlight.js": "^10.4.1", + "lodash.merge": "^4.6.2", + "strip-ansi": "^4.0.0", + "strip-indent": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@types/async-eventemitter": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz", + "integrity": "sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==", + "dev": true + }, + "@types/bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA==", + "dev": true, + "requires": { + "bignumber.js": "*" + } + }, + "@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "dev": true + }, + "@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/node": { + "version": "18.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", + "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "dev": true + }, + "@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "abortcontroller-polyfill": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", + "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==", + "dev": true + }, + "abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "dev": true, + "requires": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true + }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true + }, + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "antlr4": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz", + "integrity": "sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ==", + "dev": true + }, + "antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "ast-parents": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", + "integrity": "sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "dev": true, + "requires": { + "async": "^2.4.0" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + } + } + }, + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true + }, + "big-integer": { + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==", + "dev": true + }, + "big.js": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", + "integrity": "sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==", + "dev": true + }, + "bigint-crypto-utils": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.8.tgz", + "integrity": "sha512-+VMV9Laq8pXLBKKKK49nOoq9bfR3j7NNQAtbA617a4nw9bVLo8rsqkKMBgM2AJWlNX9fEIyYaYX+d0laqYV4tw==", + "dev": true, + "requires": { + "bigint-mod-arith": "^3.1.0" + } + }, + "bigint-mod-arith": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz", + "integrity": "sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ==", + "dev": true + }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "dev": true, + "requires": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-reverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", + "integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==", + "dev": true + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha512-3dthu5CYiVB1DEJp61FtApNnNndTckcqe4pFcLdvHtrpG+kcyekCJKg4MRiDcFW7A6AODnXB9U4dwQiCW5kzJQ==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true + }, + "bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "dev": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "requires": { + "streamsearch": "^1.1.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacheable-lookup": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz", + "integrity": "sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "requires": { + "callsites": "^2.0.0" + }, + "dependencies": { + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true + } + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "dev": true + }, + "cbor": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz", + "integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==", + "dev": true, + "requires": { + "bignumber.js": "^9.0.1", + "nofilter": "^1.0.4" + }, + "dependencies": { + "bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "dev": true + } + } + }, + "chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chai-bn": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/chai-bn/-/chai-bn-0.2.2.tgz", + "integrity": "sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg==", + "dev": true, + "requires": {} + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "change-case": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.2.tgz", + "integrity": "sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==", + "dev": true, + "requires": { + "camel-case": "^3.0.0", + "constant-case": "^2.0.0", + "dot-case": "^2.1.0", + "header-case": "^1.0.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "no-case": "^2.3.2", + "param-case": "^2.1.0", + "pascal-case": "^2.0.0", + "path-case": "^2.1.0", + "sentence-case": "^2.1.0", + "snake-case": "^2.1.0", + "swap-case": "^1.1.0", + "title-case": "^2.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, + "cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "requires": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "dev": true, + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==", + "dev": true + }, + "classic-level": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz", + "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==", + "dev": true, + "requires": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true + }, + "commander": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "constant-case": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", + "integrity": "sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==", + "dev": true, + "requires": { + "snake-case": "^2.1.0", + "upper-case": "^1.1.1" + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "dev": true, + "requires": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + } + } + }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "requires": { + "node-fetch": "2.6.7" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true + }, + "crypto-addr-codec": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", + "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", + "dev": true, + "requires": { + "base-x": "^3.0.8", + "big-integer": "1.6.36", + "blakejs": "^1.1.0", + "bs58": "^4.0.1", + "ripemd160-min": "0.0.6", + "safe-buffer": "^5.2.0", + "sha3": "^2.1.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==", + "dev": true + }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "dev": true + }, + "detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "dev": true, + "requires": { + "address": "^1.0.1", + "debug": "4" + } + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "requires": { + "heap": ">= 0.2.0" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, + "dot-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", + "integrity": "sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "emoji-regex": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", + "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + } + } + }, + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz", + "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "unbox-primitive": "^1.0.2" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "requires": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "eslint-plugin-mocha": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz", + "integrity": "sha512-xLqqWUF17llsogVOC+8C6/jvQ+4IoOREbN7ZCHuOHuD6cT5cDD4h7f2LgsZuzMAiwswWE21tO7ExaknHVDrSkw==", + "dev": true, + "requires": { + "eslint-utils": "^3.0.0", + "rambda": "^7.1.0" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.2.0.tgz", + "integrity": "sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha512-VWEI1+KJfz4Km//dadyvBBoBeSQ0MHTXPvr8UIXiLW6IanxvAV+DmlZAijZwAyggqGUfwQBeHf7tc9wzc1piSw==", + "dev": true, + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", + "dev": true + } + } + }, + "eth-gas-reporter": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz", + "integrity": "sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.0.0-beta.146", + "@solidity-parser/parser": "^0.14.0", + "cli-table3": "^0.5.0", + "colors": "1.4.0", + "ethereum-cryptography": "^1.0.3", + "ethers": "^4.0.40", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^7.1.1", + "req-cwd": "^2.0.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "dev": true, + "requires": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mocha": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", + "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + } + } + } + }, + "eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "eth-sig-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-3.0.1.tgz", + "integrity": "sha512-0Us50HiGGvZgjtWTyAI/+qTzYPMLy5Q451D0Xy68bxq1QMWdoOddDwGvsqcFT27uohKgalM9z/yxplyt+mY2iQ==", + "dev": true, + "requires": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^5.1.1", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "dev": true, + "requires": { + "js-sha3": "^0.8.0" + } + }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereum-ens": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/ethereum-ens/-/ethereum-ens-0.8.0.tgz", + "integrity": "sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg==", + "dev": true, + "requires": { + "bluebird": "^3.4.7", + "eth-ens-namehash": "^2.0.0", + "js-sha3": "^0.5.7", + "pako": "^1.0.4", + "underscore": "^1.8.3", + "web3": "^1.0.0-beta.34" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", + "dev": true + } + } + }, + "ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dev": true, + "requires": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "ethereumjs-wallet": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz", + "integrity": "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA==", + "dev": true, + "requires": { + "aes-js": "^3.1.2", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^7.1.2", + "randombytes": "^2.1.0", + "scrypt-js": "^3.0.1", + "utf8": "^3.0.0", + "uuid": "^8.3.2" + } + }, + "ethers": { + "version": "4.0.49", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz", + "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==", + "dev": true, + "requires": { + "aes-js": "3.0.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==", + "dev": true + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==", + "dev": true + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==", + "dev": true + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==", + "dev": true + } + } + }, + "ethjs-abi": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.1.tgz", + "integrity": "sha512-g2AULSDYI6nEJyJaEVEXtTimRY2aPC2fi7ddSy0W+LXvEVL8Fe1y76o43ecbgdUKwZD+xsmEgX1yJr1Ia3r1IA==", + "dev": true, + "requires": { + "bn.js": "4.11.6", + "js-sha3": "0.5.5", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true + }, + "js-sha3": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", + "integrity": "sha512-yLLwn44IVeunwjpDVTDZmQeVbB0h+dZpY2eO68B/Zik8hu6dH+rKeLxwua79GGIvW6xr8NBAcrtiUbYrTjEFTA==", + "dev": true + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "requires": { + "type": "^2.7.2" + }, + "dependencies": { + "type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true + }, + "fast-check": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.1.1.tgz", + "integrity": "sha512-3vtXinVyuUKCKFKYcwXhGE6NtGWkqF8Yh3rvMZNzmwz8EPrgoc/v4pDdLHyLnCyCI5MZpZZkDEwFyXyEONOxpA==", + "dev": true, + "requires": { + "pure-rand": "^5.0.1" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + }, + "dependencies": { + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + } + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "form-data-encoder": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz", + "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + } + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "got": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz", + "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.6.0", + "@szmarczak/http-timer": "^5.0.1", + "@types/cacheable-request": "^6.0.2", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^6.0.4", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "form-data-encoder": "1.7.1", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hardhat": { + "version": "2.12.4", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.12.4.tgz", + "integrity": "sha512-rc9S2U/4M+77LxW1Kg7oqMMmjl81tzn5rNFARhbXKUA1am/nhfMJEujOjuKvt+ZGMiZ11PYSe8gyIpB/aRNDgw==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/ethereumjs-block": "^4.0.0", + "@nomicfoundation/ethereumjs-blockchain": "^6.0.0", + "@nomicfoundation/ethereumjs-common": "^3.0.0", + "@nomicfoundation/ethereumjs-evm": "^1.0.0", + "@nomicfoundation/ethereumjs-rlp": "^4.0.0", + "@nomicfoundation/ethereumjs-statemanager": "^1.0.0", + "@nomicfoundation/ethereumjs-trie": "^5.0.0", + "@nomicfoundation/ethereumjs-tx": "^4.0.0", + "@nomicfoundation/ethereumjs-util": "^8.0.0", + "@nomicfoundation/ethereumjs-vm": "^6.0.0", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "abort-controller": "^3.0.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "chalk": "^2.4.2", + "chokidar": "^3.4.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "qs": "^6.7.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.7.3", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.4.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "dev": true, + "requires": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "solc": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", + "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", + "dev": true, + "requires": { + "command-exists": "^1.2.8", + "commander": "3.0.2", + "follow-redirects": "^1.12.1", + "fs-extra": "^0.30.0", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "dependencies": { + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "hardhat-gas-reporter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", + "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==", + "dev": true, + "requires": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + } + }, + "hardhat-ignore-warnings": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/hardhat-ignore-warnings/-/hardhat-ignore-warnings-0.2.6.tgz", + "integrity": "sha512-GQgvjprONI8VF8b85+QJ8H9v3L9TCCtQvUx+9QaRL+sCPw1cOZHfhlEz9V6Lq7GNCQMqBORVzNzUzoP/tKAEQQ==", + "dev": true, + "requires": { + "minimatch": "^5.1.0", + "node-interval-tree": "^2.0.1", + "solidity-comments": "^0.0.2" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "header-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", + "integrity": "sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.3" + } + }, + "heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true + }, + "highlightjs-solidity": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.5.tgz", + "integrity": "sha512-ReXxQSGQkODMUgHcWzVSnfDCDrL2HshOYgw3OlIYmfHeRzUPkfJTUIp95pK4CmbiNG2eMTOmNLpfCz9Zq7Cwmg==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "requires": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha512-o0PWwVCSp3O0wS6FvNr6xfBCHgt0m1tvPLFOCc2iFDKTRAXhB7m8klDf7ErowFH8POa6dVdGatKU5I1YYwzUyg==", + "dev": true + }, + "http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "requires": { + "@types/node": "^10.0.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http2-wrapper": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", + "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "dev": true, + "requires": { + "punycode": "2.1.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "immutable": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.1.tgz", + "integrity": "sha512-7WYV7Q5BTs0nlQm7tl92rDYYoyELLKHoDMBKhrxEoiV4mrfVdRz8hzPiYOzH7yWjzoVEamxRuAqhxL2PLRwZYQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true + }, + "io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "requires": { + "fp-ts": "^1.0.0" + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true + }, + "is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==", + "dev": true, + "requires": { + "lower-case": "^1.1.0" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-port-reachable": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-3.1.0.tgz", + "integrity": "sha512-vjc0SSRNZ32s9SbZBzGaiP6YVB+xglLShhgZD/FHMZUXBvQWaV9CtzgeVhjccFJrI6RAMV+LX7NYxueW/A8W5A==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==", + "dev": true, + "requires": { + "upper-case": "^1.1.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "dev": true, + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, + "keccak256": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz", + "integrity": "sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==", + "dev": true, + "requires": { + "bn.js": "^5.2.0", + "buffer": "^6.0.3", + "keccak": "^3.0.2" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, + "keyv": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "level": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", + "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", + "dev": true, + "requires": { + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" + } + }, + "level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "dev": true + }, + "level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dev": true, + "requires": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true + }, + "lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==", + "dev": true, + "requires": { + "lower-case": "^1.1.2" + } + }, + "lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true + }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true + }, + "mcl-wasm": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", + "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "memory-level": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", + "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", + "dev": true, + "requires": { + "abstract-level": "^1.0.0", + "functional-red-black-tree": "^1.0.1", + "module-error": "^1.0.1" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "merkletreejs": { + "version": "0.2.32", + "resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.32.tgz", + "integrity": "sha512-TostQBiwYRIwSE5++jGmacu3ODcKAgqb0Y/pnIohXS7sWxh1gCkSptbmF1a43faehRDpcHf7J/kv0Ml2D/zblQ==", + "dev": true, + "requires": { + "bignumber.js": "^9.0.1", + "buffer-reverse": "^1.0.1", + "crypto-js": "^3.1.9-1", + "treeify": "^1.1.0", + "web3-utils": "^1.3.4" + }, + "dependencies": { + "bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "dev": true + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "dev": true, + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha512-Hepn5kb1lJPtVW84RFT40YG1OddBNTOVUZR2bzQUHc+Z03en8/3uX0+060JDhcEzyO08HmipsN9DcnFMxhIL9w==", + "dev": true, + "requires": { + "mkdirp": "*" + } + }, + "mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "requires": { + "obliterator": "^2.0.0" + } + }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==", + "dev": true + }, + "module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "dev": true, + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "dev": true, + "requires": { + "varint": "^5.0.0" + } + }, + "multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + }, + "dependencies": { + "multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "dev": true, + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + } + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true + }, + "nano-base32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nano-base32/-/nano-base32-1.0.1.tgz", + "integrity": "sha512-sxEtoTqAPdjWVGv71Q17koMFGsOMSiHsIFEvzOM7cNp8BXB4AnEwmDabm5dorusJf/v1z7QxaZYxUorU9RKaAw==", + "dev": true + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew==", + "dev": true + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, + "napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true + }, + "node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, + "node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true + }, + "node-interval-tree": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-interval-tree/-/node-interval-tree-2.1.2.tgz", + "integrity": "sha512-bJ9zMDuNGzVQg1xv0bCPzyEDxHgbrx7/xGj6CDokvizZZmastPsOh0JJLuY8wA5q2SfX1TLNMk7XNV8WxbGxzA==", + "dev": true, + "requires": { + "shallowequal": "^1.1.0" + } + }, + "nofilter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz", + "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz", + "integrity": "sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==", + "dev": true, + "requires": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true + }, + "oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA==", + "dev": true, + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, + "parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "requires": { + "entities": "^4.4.0" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascal-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", + "integrity": "sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==", + "dev": true, + "requires": { + "camel-case": "^3.0.0", + "upper-case-first": "^1.1.0" + } + }, + "path-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", + "integrity": "sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", + "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "dev": true + }, + "prettier-plugin-solidity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.0.tgz", + "integrity": "sha512-5gq0T49ifvXH/6x1STuKyWjTUgi6ICoV65yNtKlg/vZEvocFtSpByJOJICBfqPwNsnv4vhhWIqkLGSUJmWum2w==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.14.5", + "emoji-regex": "^10.2.1", + "escape-string-regexp": "^4.0.0", + "semver": "^7.3.8", + "solidity-comments-extractor": "^0.0.7", + "string-width": "^4.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "requires": { + "asap": "~2.0.6" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==", + "dev": true + }, + "pure-rand": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.5.tgz", + "integrity": "sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==", + "dev": true + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "rambda": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.4.0.tgz", + "integrity": "sha512-A9hihu7dUTLOUCM+I8E61V4kRXnN4DwYeK0DwCBydC1MqNI1PidyAtbtpsJlBBzK4icSctEcCQ1bGcLpBuETUQ==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "requires": { + "minimatch": "^3.0.5" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "requires": { + "req-from": "^2.0.0" + } + }, + "req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + } + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "request-promise-native": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", + "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", + "dev": true, + "requires": { + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha512-H7AkJWMobeskkttHyhTVtS0fxpFLjxhbfMa6Bk3wimP7sdPRGL3EyCg3sAQenFfAe+xQ+oAc85Nmtvq0ROM83Q==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==", + "dev": true + }, + "rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "requires": { + "bn.js": "^5.2.0" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==", + "dev": true + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true + }, + "secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, + "requires": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "sentence-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", + "integrity": "sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case-first": "^1.1.2" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "dev": true, + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "requires": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + } + }, + "sha3": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "dev": true, + "requires": { + "buffer": "6.0.3" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz", + "integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==", + "dev": true, + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + } + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } + }, + "snake-case": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "integrity": "sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "solc": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz", + "integrity": "sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA==", + "dev": true, + "requires": { + "fs-extra": "^0.30.0", + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha512-LqodLrnIDM3IFT+Hf/5sxBnEGECrfdC1uIbgZeJmESCSo4HoCAaKEus8MylXHAkdacGc0ye+Qa+dpkuom8uVYA==", + "dev": true, + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } + }, + "solhint": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.3.7.tgz", + "integrity": "sha512-NjjjVmXI3ehKkb3aNtRJWw55SUVJ8HMKKodwe0HnejA+k0d2kmhw7jvpa+MCTbcEgt8IWSwx0Hu6aCo/iYOZzQ==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.14.1", + "ajv": "^6.6.1", + "antlr4": "4.7.1", + "ast-parents": "0.0.1", + "chalk": "^2.4.2", + "commander": "2.18.0", + "cosmiconfig": "^5.0.7", + "eslint": "^5.6.0", + "fast-diff": "^1.1.2", + "glob": "^7.1.3", + "ignore": "^4.0.6", + "js-yaml": "^3.12.0", + "lodash": "^4.17.11", + "prettier": "^1.14.3", + "semver": "^6.3.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "solidity-ast": { + "version": "0.4.39", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.39.tgz", + "integrity": "sha512-91d4HMzV9x3ZG1fXRtAFFq2UjJrQXkyWdrmzXqBlueOSGB+v+0+iiLfZIPnTE0apndG2zm23qkZQJf8IbRrf7w==", + "dev": true + }, + "solidity-comments": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments/-/solidity-comments-0.0.2.tgz", + "integrity": "sha512-G+aK6qtyUfkn1guS8uzqUeua1dURwPlcOjoTYW/TwmXAcE7z/1+oGCfZUdMSe4ZMKklNbVZNiG5ibnF8gkkFfw==", + "dev": true, + "requires": { + "solidity-comments-darwin-arm64": "0.0.2", + "solidity-comments-darwin-x64": "0.0.2", + "solidity-comments-freebsd-x64": "0.0.2", + "solidity-comments-linux-arm64-gnu": "0.0.2", + "solidity-comments-linux-arm64-musl": "0.0.2", + "solidity-comments-linux-x64-gnu": "0.0.2", + "solidity-comments-linux-x64-musl": "0.0.2", + "solidity-comments-win32-arm64-msvc": "0.0.2", + "solidity-comments-win32-ia32-msvc": "0.0.2", + "solidity-comments-win32-x64-msvc": "0.0.2" + } + }, + "solidity-comments-darwin-arm64": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-darwin-arm64/-/solidity-comments-darwin-arm64-0.0.2.tgz", + "integrity": "sha512-HidWkVLSh7v+Vu0CA7oI21GWP/ZY7ro8g8OmIxE8oTqyMwgMbE8F1yc58Sj682Hj199HCZsjmtn1BE4PCbLiGA==", + "dev": true, + "optional": true + }, + "solidity-comments-darwin-x64": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-darwin-x64/-/solidity-comments-darwin-x64-0.0.2.tgz", + "integrity": "sha512-Zjs0Ruz6faBTPT6fBecUt6qh4CdloT8Bwoc0+qxRoTn9UhYscmbPQkUgQEbS0FQPysYqVzzxJB4h1Ofbf4wwtA==", + "dev": true, + "optional": true + }, + "solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "dev": true + }, + "solidity-comments-freebsd-x64": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-freebsd-x64/-/solidity-comments-freebsd-x64-0.0.2.tgz", + "integrity": "sha512-8Qe4mpjuAxFSwZJVk7B8gAoLCdbtS412bQzBwk63L8dmlHogvE39iT70aAk3RHUddAppT5RMBunlPUCFYJ3ZTw==", + "dev": true, + "optional": true + }, + "solidity-comments-linux-arm64-gnu": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-arm64-gnu/-/solidity-comments-linux-arm64-gnu-0.0.2.tgz", + "integrity": "sha512-spkb0MZZnmrP+Wtq4UxP+nyPAVRe82idOjqndolcNR0S9Xvu4ebwq+LvF4HiUgjTDmeiqYiFZQ8T9KGdLSIoIg==", + "dev": true, + "optional": true + }, + "solidity-comments-linux-arm64-musl": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-arm64-musl/-/solidity-comments-linux-arm64-musl-0.0.2.tgz", + "integrity": "sha512-guCDbHArcjE+JDXYkxx5RZzY1YF6OnAKCo+sTC5fstyW/KGKaQJNPyBNWuwYsQiaEHpvhW1ha537IvlGek8GqA==", + "dev": true, + "optional": true + }, + "solidity-comments-linux-x64-gnu": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-x64-gnu/-/solidity-comments-linux-x64-gnu-0.0.2.tgz", + "integrity": "sha512-zIqLehBK/g7tvrFmQljrfZXfkEeLt2v6wbe+uFu6kH/qAHZa7ybt8Vc0wYcmjo2U0PeBm15d79ee3AkwbIjFdQ==", + "dev": true, + "optional": true + }, + "solidity-comments-linux-x64-musl": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-linux-x64-musl/-/solidity-comments-linux-x64-musl-0.0.2.tgz", + "integrity": "sha512-R9FeDloVlFGTaVkOlELDVC7+1Tjx5WBPI5L8r0AGOPHK3+jOcRh6sKYpI+VskSPDc3vOO46INkpDgUXrKydlIw==", + "dev": true, + "optional": true + }, + "solidity-comments-win32-arm64-msvc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-win32-arm64-msvc/-/solidity-comments-win32-arm64-msvc-0.0.2.tgz", + "integrity": "sha512-QnWJoCQcJj+rnutULOihN9bixOtYWDdF5Rfz9fpHejL1BtNjdLW1om55XNVHGAHPqBxV4aeQQ6OirKnp9zKsug==", + "dev": true, + "optional": true + }, + "solidity-comments-win32-ia32-msvc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-win32-ia32-msvc/-/solidity-comments-win32-ia32-msvc-0.0.2.tgz", + "integrity": "sha512-vUg4nADtm/NcOtlIymG23NWJUSuMsvX15nU7ynhGBsdKtt8xhdP3C/zA6vjDk8Jg+FXGQL6IHVQ++g/7rSQi0w==", + "dev": true, + "optional": true + }, + "solidity-comments-win32-x64-msvc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/solidity-comments-win32-x64-msvc/-/solidity-comments-win32-x64-msvc-0.0.2.tgz", + "integrity": "sha512-36j+KUF4V/y0t3qatHm/LF5sCUCBx2UndxE1kq5bOzh/s+nQgatuyB+Pd5BfuPQHdWu2KaExYe20FlAa6NL7+Q==", + "dev": true, + "optional": true + }, + "solidity-coverage": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz", + "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.14.1", + "chalk": "^2.4.2", + "death": "^1.1.0", + "detect-port": "^1.3.0", + "difflib": "^0.2.4", + "fs-extra": "^8.1.0", + "ghost-testrpc": "^0.0.2", + "global-modules": "^2.0.0", + "globby": "^10.0.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.15", + "mocha": "7.1.2", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "dependencies": { + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", + "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.2.0" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, + "requires": { + "chalk": "^2.4.2" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mocha": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", + "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "chokidar": "3.3.0", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "3.0.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.5", + "ms": "2.1.1", + "node-environment-flags": "1.0.6", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + }, + "dependencies": { + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "readdirp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", + "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + } + } + } + }, + "solidity-docgen": { + "version": "0.6.0-beta.32", + "resolved": "https://registry.npmjs.org/solidity-docgen/-/solidity-docgen-0.6.0-beta.32.tgz", + "integrity": "sha512-LGcosbgqxrqTo9winVq3+Xz1shS9k4p+RKwqPVPI0HtjfsKF9Mc5GzBzOUhce9uwzy0yJfhf832whfY1UKt4Aw==", + "dev": true, + "requires": { + "handlebars": "^4.7.7", + "solidity-ast": "^0.4.38" + } + }, + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + } + } + }, + "stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "requires": { + "type-fest": "^0.7.1" + }, + "dependencies": { + "type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "dev": true + }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "swarm-js": { + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.42.tgz", + "integrity": "sha512-BV7c/dVlA3R6ya1lMlSSNPLYrntt0LUq4YMgy3iwpCIc6rZnS5W2wUoctarZ5pXlpKtxDDf9hNziEkcfrxdhqQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^11.8.5", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + }, + "dependencies": { + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + } + } + }, + "sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "requires": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + } + }, + "sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "requires": { + "get-port": "^3.1.0" + } + }, + "table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "dev": true, + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + } + }, + "testrpc": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/testrpc/-/testrpc-0.0.1.tgz", + "integrity": "sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "requires": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "dev": true + }, + "title-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.0.3" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true + }, + "tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "dev": true + }, + "undici": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", + "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", + "dev": true, + "requires": { + "busboy": "^1.6.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "dev": true + }, + "upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", + "dev": true, + "requires": { + "upper-case": "^1.1.1" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==", + "dev": true + }, + "utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "dev": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "dev": true + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "web3": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.8.1.tgz", + "integrity": "sha512-tAqFsQhGv340C9OgRJIuoScN7f7wa1tUvsnnDUMt9YE6J4gcm7TV2Uwv+KERnzvV+xgdeuULYpsioRRNKrUvoQ==", + "dev": true, + "requires": { + "web3-bzz": "1.8.1", + "web3-core": "1.8.1", + "web3-eth": "1.8.1", + "web3-eth-personal": "1.8.1", + "web3-net": "1.8.1", + "web3-shh": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-bzz": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.8.1.tgz", + "integrity": "sha512-dJJHS84nvpoxv6ijTMkdUSlRr5beCXNtx4UZcrFLHBva8dT63QEtKdLyDt2AyMJJdVzTCk78uir/6XtVWrdS6w==", + "dev": true, + "requires": { + "@types/node": "^12.12.6", + "got": "12.1.0", + "swarm-js": "^0.1.40" + }, + "dependencies": { + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + } + } + }, + "web3-core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.8.1.tgz", + "integrity": "sha512-LbRZlJH2N6nS3n3Eo9Y++25IvzMY7WvYnp4NM/Ajhh97dAdglYs6rToQ2DbL2RLvTYmTew4O/y9WmOk4nq9COw==", + "dev": true, + "requires": { + "@types/bn.js": "^5.1.0", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-requestmanager": "1.8.1", + "web3-utils": "1.8.1" + }, + "dependencies": { + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + }, + "bignumber.js": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "dev": true + } + } + }, + "web3-core-helpers": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.8.1.tgz", + "integrity": "sha512-ClzNO6T1S1gifC+BThw0+GTfcsjLEY8T1qUp6Ly2+w4PntAdNtKahxWKApWJ0l9idqot/fFIDXwO3Euu7I0Xqw==", + "dev": true, + "requires": { + "web3-eth-iban": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-core-method": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.8.1.tgz", + "integrity": "sha512-oYGRodktfs86NrnFwaWTbv2S38JnpPslFwSSARwFv4W9cjbGUW3LDeA5MKD/dRY+ssZ5OaekeMsUCLoGhX68yA==", + "dev": true, + "requires": { + "@ethersproject/transactions": "^5.6.2", + "web3-core-helpers": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-core-promievent": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.8.1.tgz", + "integrity": "sha512-9mxqHlgB0MrZI4oUIRFkuoJMNj3E7btjrMv3sMer/Z9rYR1PfoSc1aAokw4rxKIcAh+ylVtd/acaB2HKB7aRPg==", + "dev": true, + "requires": { + "eventemitter3": "4.0.4" + } + }, + "web3-core-requestmanager": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.8.1.tgz", + "integrity": "sha512-x+VC2YPPwZ1khvqA6TA69LvfFCOZXsoUVOxmTx/vIN22PrY9KzKhxcE7pBSiGhmab1jtmRYXUbcQSVpAXqL8cw==", + "dev": true, + "requires": { + "util": "^0.12.0", + "web3-core-helpers": "1.8.1", + "web3-providers-http": "1.8.1", + "web3-providers-ipc": "1.8.1", + "web3-providers-ws": "1.8.1" + } + }, + "web3-core-subscriptions": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.8.1.tgz", + "integrity": "sha512-bmCMq5OeA3E2vZUh8Js1HcJbhwtsE+yeMqGC4oIZB3XsL5SLqyKLB/pU+qUYqQ9o4GdcrFTDPhPg1bgvf7p1Pw==", + "dev": true, + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.1" + } + }, + "web3-eth": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.8.1.tgz", + "integrity": "sha512-LgyzbhFqiFRd8M8sBXoFN4ztzOnkeckl3H/9lH5ek7AdoRMhBg7tYpYRP3E5qkhd/q+yiZmcUgy1AF6NHrC1wg==", + "dev": true, + "requires": { + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-eth-accounts": "1.8.1", + "web3-eth-contract": "1.8.1", + "web3-eth-ens": "1.8.1", + "web3-eth-iban": "1.8.1", + "web3-eth-personal": "1.8.1", + "web3-net": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-eth-abi": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.8.1.tgz", + "integrity": "sha512-0mZvCRTIG0UhDhJwNQJgJxu4b4DyIpuMA0GTfqxqeuqzX4Q/ZvmoNurw0ExTfXaGPP82UUmmdkRi6FdZOx+C6w==", + "dev": true, + "requires": { + "@ethersproject/abi": "^5.6.3", + "web3-utils": "1.8.1" + } + }, + "web3-eth-accounts": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.8.1.tgz", + "integrity": "sha512-mgzxSYgN54/NsOFBO1Fq1KkXp1S5KlBvI/DlgvajU72rupoFMq6Cu6Yp9GUaZ/w2ij9PzEJuFJk174XwtfMCmg==", + "dev": true, + "requires": { + "@ethereumjs/common": "2.5.0", + "@ethereumjs/tx": "3.3.2", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "^9.0.0", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-utils": "1.8.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "dev": true, + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "dev": true + } + } + }, + "web3-eth-contract": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.8.1.tgz", + "integrity": "sha512-1wphnl+/xwCE2io44JKnN+ti3oa47BKRiVzvWd42icwRbcpFfRxH9QH+aQX3u8VZIISNH7dAkTWpGIIJgGFTmg==", + "dev": true, + "requires": { + "@types/bn.js": "^5.1.0", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-eth-ens": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.8.1.tgz", + "integrity": "sha512-FT8xTI9uN8RxeBQa/W8pLa2aoFh4+EE34w7W2271LICKzla1dtLyb6XSdn48vsUcPmhWsTVk9mO9RTU0l4LGQQ==", + "dev": true, + "requires": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-promievent": "1.8.1", + "web3-eth-abi": "1.8.1", + "web3-eth-contract": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-eth-iban": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.8.1.tgz", + "integrity": "sha512-DomoQBfvIdtM08RyMGkMVBOH0vpOIxSSQ+jukWk/EkMLGMWJtXw/K2c2uHAeq3L/VPWNB7zXV2DUEGV/lNE2Dg==", + "dev": true, + "requires": { + "bn.js": "^5.2.1", + "web3-utils": "1.8.1" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "web3-eth-personal": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.8.1.tgz", + "integrity": "sha512-myIYMvj7SDIoV9vE5BkVdon3pya1WinaXItugoii2VoTcQNPOtBxmYVH+XS5ErzCJlnxzphpQrkywyY64bbbCA==", + "dev": true, + "requires": { + "@types/node": "^12.12.6", + "web3-core": "1.8.1", + "web3-core-helpers": "1.8.1", + "web3-core-method": "1.8.1", + "web3-net": "1.8.1", + "web3-utils": "1.8.1" + }, + "dependencies": { + "@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true + } + } + }, + "web3-net": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.8.1.tgz", + "integrity": "sha512-LyEJAwogdFo0UAXZqoSJGFjopdt+kLw0P00FSZn2yszbgcoI7EwC+nXiOsEe12xz4LqpYLOtbR7+gxgiTVjjHQ==", + "dev": true, + "requires": { + "web3-core": "1.8.1", + "web3-core-method": "1.8.1", + "web3-utils": "1.8.1" + } + }, + "web3-providers-http": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.8.1.tgz", + "integrity": "sha512-1Zyts4O9W/UNEPkp+jyL19Jc3D15S4yp8xuLTjVhcUEAlHo24NDWEKxtZGUuHk4HrKL2gp8OlsDbJ7MM+ESDgg==", + "dev": true, + "requires": { + "abortcontroller-polyfill": "^1.7.3", + "cross-fetch": "^3.1.4", + "es6-promise": "^4.2.8", + "web3-core-helpers": "1.8.1" + } + }, + "web3-providers-ipc": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.8.1.tgz", + "integrity": "sha512-nw/W5nclvi+P2z2dYkLWReKLnocStflWqFl+qjtv0xn3MrUTyXMzSF0+61i77+16xFsTgzo4wS/NWIOVkR0EFA==", + "dev": true, + "requires": { + "oboe": "2.1.5", + "web3-core-helpers": "1.8.1" + } + }, + "web3-providers-ws": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.8.1.tgz", + "integrity": "sha512-TNefIDAMpdx57+YdWpYZ/xdofS0P+FfKaDYXhn24ie/tH9G+AB+UBSOKnjN0KSadcRSCMBwGPRiEmNHPavZdsA==", + "dev": true, + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.8.1", + "websocket": "^1.0.32" + } + }, + "web3-shh": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.8.1.tgz", + "integrity": "sha512-sqHgarnfcY2Qt3PYS4R6YveHrDy7hmL09yeLLHHCI+RKirmjLVqV0rc5LJWUtlbYI+kDoa5gbgde489M9ZAC0g==", + "dev": true, + "requires": { + "web3-core": "1.8.1", + "web3-core-method": "1.8.1", + "web3-core-subscriptions": "1.8.1", + "web3-net": "1.8.1" + } + }, + "web3-utils": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.1.tgz", + "integrity": "sha512-LgnM9p6V7rHHUGfpMZod+NST8cRfGzJ1BTXAyNo7A9cJX9LczBfSRxJp+U/GInYe9mby40t3v22AJdlELibnsQ==", + "dev": true, + "requires": { + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereumjs-util": "^7.1.0", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "dependencies": { + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + } + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dev": true, + "requires": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "dev": true + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "requires": {} + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dev": true, + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "dev": true, + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "dev": true, + "requires": { + "xhr-request": "^1.1.0" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/lib/openzeppelin-contracts/package.json b/lib/openzeppelin-contracts/package.json new file mode 100644 index 0000000..4bc8f6e --- /dev/null +++ b/lib/openzeppelin-contracts/package.json @@ -0,0 +1,92 @@ +{ + "name": "openzeppelin-solidity", + "description": "Secure Smart Contract library for Solidity", + "version": "4.8.0", + "files": [ + "/contracts/**/*.sol", + "/build/contracts/*.json", + "!/contracts/mocks/**/*" + ], + "bin": { + "openzeppelin-contracts-migrate-imports": "scripts/migrate-imports.js" + }, + "scripts": { + "compile": "hardhat compile", + "coverage": "env COVERAGE=true hardhat coverage", + "docs": "npm run prepare-docs && oz-docs", + "docs:watch": "oz-docs watch contracts 'docs/templates' docs/config.js", + "prepare-docs": "scripts/prepare-docs.sh", + "lint": "npm run lint:js && npm run lint:sol", + "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix", + "lint:js": "eslint --ignore-path .gitignore .", + "lint:js:fix": "eslint --ignore-path .gitignore . --fix", + "lint:sol": "solhint '{contracts,test}/**/*.sol' && prettier -c '{contracts,test}/**/*.sol'", + "lint:sol:fix": "prettier --write '{contracts,test}/**/*.sol'", + "clean": "hardhat clean && rimraf build contracts/build", + "prepare": "scripts/prepare.sh", + "prepack": "scripts/prepack.sh", + "generate": "scripts/generate/run.js", + "release": "scripts/release/release.sh", + "version": "scripts/release/version.sh", + "test": "hardhat test", + "test:inheritance": "scripts/checks/inheritance-ordering.js artifacts/build-info/*", + "test:generation": "scripts/checks/generation.sh", + "gas-report": "env ENABLE_GAS_REPORT=true npm run test", + "slither": "npm run clean && slither . --detect reentrancy-eth,reentrancy-no-eth,reentrancy-unlimited-gas" + }, + "repository": { + "type": "git", + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts.git" + }, + "keywords": [ + "solidity", + "ethereum", + "smart", + "contracts", + "security", + "zeppelin" + ], + "author": "OpenZeppelin Community ", + "license": "MIT", + "bugs": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts/issues" + }, + "homepage": "https://openzeppelin.com/contracts/", + "devDependencies": { + "@nomicfoundation/hardhat-network-helpers": "^1.0.3", + "@nomiclabs/hardhat-truffle5": "^2.0.5", + "@nomiclabs/hardhat-web3": "^2.0.0", + "@openzeppelin/docs-utils": "^0.1.3", + "@openzeppelin/test-helpers": "^0.5.13", + "chai": "^4.2.0", + "eslint": "^7.32.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-mocha": "^10.0.3", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.2.0", + "eth-sig-util": "^3.0.0", + "ethereumjs-util": "^7.0.7", + "ethereumjs-wallet": "^1.0.1", + "glob": "^8.0.3", + "graphlib": "^2.1.8", + "hardhat": "^2.9.1", + "hardhat-gas-reporter": "^1.0.4", + "hardhat-ignore-warnings": "^0.2.0", + "keccak256": "^1.0.2", + "lodash.startcase": "^4.4.0", + "lodash.zip": "^4.2.0", + "merkletreejs": "^0.2.13", + "micromatch": "^4.0.2", + "prettier": "^2.3.0", + "prettier-plugin-solidity": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "solhint": "^3.3.6", + "solidity-ast": "^0.4.25", + "solidity-coverage": "^0.8.0", + "solidity-docgen": "^0.6.0-beta.29", + "web3": "^1.3.0", + "yargs": "^17.0.0" + } +} diff --git a/lib/openzeppelin-contracts/renovate.json b/lib/openzeppelin-contracts/renovate.json new file mode 100644 index 0000000..0ec3e85 --- /dev/null +++ b/lib/openzeppelin-contracts/renovate.json @@ -0,0 +1,11 @@ +{ + "extends": [ + "github>OpenZeppelin/code-style" + ], + "packageRules": [ + { + "extends": ["packages:eslint"], + "enabled": false + } + ] +} diff --git a/lib/openzeppelin-contracts/scripts/checks/compareGasReports.js b/lib/openzeppelin-contracts/scripts/checks/compareGasReports.js new file mode 100644 index 0000000..5e3a0e7 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/checks/compareGasReports.js @@ -0,0 +1,211 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const chalk = require('chalk'); +const { argv } = require('yargs') + .env() + .options({ + style: { + type: 'string', + choices: [ 'shell', 'markdown' ], + default: 'shell', + }, + }); + +// Deduce base tx cost from the percentage denominator +const BASE_TX_COST = 21000; + +// Utilities +function sum (...args) { + return args.reduce((a, b) => a + b, 0); +} + +function average (...args) { + return sum(...args) / args.length; +} + +function variation (current, previous, offset = 0) { + return { + value: current, + delta: current - previous, + prcnt: 100 * (current - previous) / (previous - offset), + }; +} + +// Report class +class Report { + // Read report file + static load (filepath) { + return JSON.parse(fs.readFileSync(filepath, 'utf8')); + } + + // Compare two reports + static compare (update, ref, opts = { hideEqual: true }) { + if (JSON.stringify(update.config.metadata) !== JSON.stringify(ref.config.metadata)) { + throw new Error('Reports produced with non matching metadata'); + } + + const deployments = update.info.deployments + .map(contract => Object.assign( + contract, + { previousVersion: ref.info.deployments.find(({ name }) => name === contract.name) }, + )) + .filter(contract => contract.gasData?.length && contract.previousVersion?.gasData?.length) + .flatMap(contract => [{ + contract: contract.name, + method: '[bytecode length]', + avg: variation(contract.bytecode.length / 2 - 1, contract.previousVersion.bytecode.length / 2 - 1), + }, { + contract: contract.name, + method: '[construction cost]', + avg: variation( + ...[contract.gasData, contract.previousVersion.gasData].map(x => Math.round(average(...x))), + BASE_TX_COST), + }]) + .sort((a, b) => `${a.contract}:${a.method}`.localeCompare(`${b.contract}:${b.method}`)); + + const methods = Object.keys(update.info.methods) + .filter(key => ref.info.methods[key]) + .filter(key => update.info.methods[key].numberOfCalls > 0) + .filter(key => update.info.methods[key].numberOfCalls === ref.info.methods[key].numberOfCalls) + .map(key => ({ + contract: ref.info.methods[key].contract, + method: ref.info.methods[key].fnSig, + min: variation(...[update, ref].map(x => Math.min(...x.info.methods[key].gasData)), BASE_TX_COST), + max: variation(...[update, ref].map(x => Math.max(...x.info.methods[key].gasData)), BASE_TX_COST), + avg: variation(...[update, ref].map(x => Math.round(average(...x.info.methods[key].gasData))), BASE_TX_COST), + })) + .sort((a, b) => `${a.contract}:${a.method}`.localeCompare(`${b.contract}:${b.method}`)); + + return [].concat(deployments, methods) + .filter(row => !opts.hideEqual || row.min?.delta || row.max?.delta || row.avg?.delta); + } +} + +// Display +function center (text, length) { + return text.padStart((text.length + length) / 2).padEnd(length); +} + +function plusSign (num) { + return num > 0 ? '+' : ''; +} + +function formatCellShell (cell) { + const format = chalk[cell?.delta > 0 ? 'red' : cell?.delta < 0 ? 'green' : 'reset']; + return [ + format((!isFinite(cell?.value) ? '-' : cell.value.toString()).padStart(8)), + format((!isFinite(cell?.delta) ? '-' : plusSign(cell.delta) + cell.delta.toString()).padStart(8)), + format((!isFinite(cell?.prcnt) ? '-' : plusSign(cell.prcnt) + cell.prcnt.toFixed(2) + '%').padStart(8)), + ]; +} + +function formatCmpShell (rows) { + const contractLength = Math.max(8, ...rows.map(({ contract }) => contract.length)); + const methodLength = Math.max(7, ...rows.map(({ method }) => method.length)); + + const COLS = [ + { txt: '', length: 0 }, + { txt: 'Contract', length: contractLength }, + { txt: 'Method', length: methodLength }, + { txt: 'Min', length: 30 }, + { txt: 'Max', length: 30 }, + { txt: 'Avg', length: 30 }, + { txt: '', length: 0 }, + ]; + const HEADER = COLS.map(entry => chalk.bold(center(entry.txt, entry.length || 0))).join(' | ').trim(); + const SEPARATOR = COLS.map(({ length }) => length > 0 ? '-'.repeat(length + 2) : '').join('|').trim(); + + return [ + '', + HEADER, + ...rows.map(entry => [ + '', + chalk.grey(entry.contract.padEnd(contractLength)), + entry.method.padEnd(methodLength), + ...formatCellShell(entry.min), + ...formatCellShell(entry.max), + ...formatCellShell(entry.avg), + '', + ].join(' | ').trim()), + '', + ].join(`\n${SEPARATOR}\n`).trim(); +} + +function alignPattern (align) { + switch (align) { + case 'left': + case undefined: + return ':-'; + case 'right': + return '-:'; + case 'center': + return ':-:'; + } +} + +function trend (value) { + return value > 0 + ? ':x:' + : value < 0 + ? ':heavy_check_mark:' + : ':heavy_minus_sign:'; +} + +function formatCellMarkdown (cell) { + return [ + (!isFinite(cell?.value) ? '-' : cell.value.toString()), + (!isFinite(cell?.delta) ? '-' : plusSign(cell.delta) + cell.delta.toString()), + (!isFinite(cell?.prcnt) ? '-' : plusSign(cell.prcnt) + cell.prcnt.toFixed(2) + '%' + trend(cell.delta)), + ]; +} + +function formatCmpMarkdown (rows) { + const COLS = [ + { txt: '' }, + { txt: 'Contract', align: 'left' }, + { txt: 'Method', align: 'left' }, + { txt: 'Min', align: 'right' }, + { txt: '(+/-)', align: 'right' }, + { txt: '%', align: 'right' }, + { txt: 'Max', align: 'right' }, + { txt: '(+/-)', align: 'right' }, + { txt: '%', align: 'right' }, + { txt: 'Avg', align: 'right' }, + { txt: '(+/-)', align: 'right' }, + { txt: '%', align: 'right' }, + { txt: '' }, + ]; + const HEADER = COLS.map(entry => entry.txt).join(' | ').trim(); + const SEPARATOR = COLS.map(entry => entry.txt ? alignPattern(entry.align) : '').join('|').trim(); + + return [ + '# Changes to gas costs', + '', + HEADER, + SEPARATOR, + rows.map(entry => [ + '', + entry.contract, + entry.method, + ...formatCellMarkdown(entry.min), + ...formatCellMarkdown(entry.max), + ...formatCellMarkdown(entry.avg), + '', + ].join(' | ').trim()).join('\n'), + '', + ].join('\n').trim(); +} + +// MAIN +const report = Report.compare(Report.load(argv._[0]), Report.load(argv._[1])); + +switch (argv.style) { +case 'markdown': + console.log(formatCmpMarkdown(report)); + break; +case 'shell': +default: + console.log(formatCmpShell(report)); + break; +} diff --git a/lib/openzeppelin-contracts/scripts/checks/generation.sh b/lib/openzeppelin-contracts/scripts/checks/generation.sh new file mode 100644 index 0000000..00d609f --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/checks/generation.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail + +npm run generate +git diff -R --exit-code diff --git a/lib/openzeppelin-contracts/scripts/checks/inheritance-ordering.js b/lib/openzeppelin-contracts/scripts/checks/inheritance-ordering.js new file mode 100644 index 0000000..3ade740 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/checks/inheritance-ordering.js @@ -0,0 +1,50 @@ +#!/usr/bin/env node + +const path = require('path'); +const graphlib = require('graphlib'); +const { findAll } = require('solidity-ast/utils'); +const { _: artifacts } = require('yargs').argv; + +for (const artifact of artifacts) { + const { output: solcOutput } = require(path.resolve(__dirname, '../..', artifact)); + + const graph = new graphlib.Graph({ directed: true }); + const names = {}; + const linearized = []; + + for (const source in solcOutput.contracts) { + if (source.includes('/mocks/')) { + continue; + } + + for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) { + names[contractDef.id] = contractDef.name; + linearized.push(contractDef.linearizedBaseContracts); + + contractDef.linearizedBaseContracts.forEach((c1, i, contracts) => contracts.slice(i + 1).forEach(c2 => { + graph.setEdge(c1, c2); + })); + } + } + + /// graphlib.alg.findCycles will not find minimal cycles. + /// We are only interested int cycles of lengths 2 (needs proof) + graph.nodes().forEach((x, i, nodes) => nodes + .slice(i + 1) + .filter(y => graph.hasEdge(x, y) && graph.hasEdge(y, x)) + .forEach(y => { + console.log(`Conflict between ${names[x]} and ${names[y]} detected in the following dependency chains:`); + linearized + .filter(chain => chain.includes(parseInt(x)) && chain.includes(parseInt(y))) + .forEach(chain => { + const comp = chain.indexOf(parseInt(x)) < chain.indexOf(parseInt(y)) ? '>' : '<'; + console.log(`- ${names[x]} ${comp} ${names[y]} in ${names[chain.find(Boolean)]}`); + // console.log(`- ${names[x]} ${comp} ${names[y]}: ${chain.reverse().map(id => names[id]).join(', ')}`); + }); + process.exitCode = 1; + })); +} + +if (!process.exitCode) { + console.log('Contract ordering is consistent.'); +} diff --git a/lib/openzeppelin-contracts/scripts/gen-nav.js b/lib/openzeppelin-contracts/scripts/gen-nav.js new file mode 100644 index 0000000..b39d23e --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/gen-nav.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +const path = require('path'); +const glob = require('glob'); +const startCase = require('lodash.startcase'); + +const baseDir = process.argv[2]; + +const files = glob.sync(baseDir + '/**/*.adoc').map(f => path.relative(baseDir, f)); + +console.log('.API'); + +function getPageTitle (directory) { + switch (directory) { + case 'metatx': + return 'Meta Transactions'; + case 'common': + return 'Common (Tokens)'; + default: + return startCase(directory); + } +} + +const links = files.map((file) => { + const doc = file.replace(baseDir, ''); + const title = path.parse(file).name; + + return { + xref: `* xref:${doc}[${getPageTitle(title)}]`, + title, + }; +}); + +// Case-insensitive sort based on titles (so 'token/ERC20' gets sorted as 'erc20') +const sortedLinks = links.sort(function (a, b) { + return a.title.toLowerCase().localeCompare(b.title.toLowerCase(), undefined, { numeric: true }); +}); + +for (const link of sortedLinks) { + console.log(link.xref); +} diff --git a/lib/openzeppelin-contracts/scripts/generate/format-lines.js b/lib/openzeppelin-contracts/scripts/generate/format-lines.js new file mode 100644 index 0000000..e1f1823 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/format-lines.js @@ -0,0 +1,16 @@ +function formatLines (...lines) { + return [...indentEach(0, lines)].join('\n') + '\n'; +} + +function *indentEach (indent, lines) { + for (const line of lines) { + if (Array.isArray(line)) { + yield * indentEach(indent + 1, line); + } else { + const padding = ' '.repeat(indent); + yield * line.split('\n').map(subline => subline === '' ? '' : padding + subline); + } + } +} + +module.exports = formatLines; diff --git a/lib/openzeppelin-contracts/scripts/generate/run.js b/lib/openzeppelin-contracts/scripts/generate/run.js new file mode 100644 index 0000000..60dd795 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/run.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node + +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const format = require('./format-lines'); + +function getVersion (path) { + try { + return fs + .readFileSync(path, 'utf8') + .match(/\/\/ OpenZeppelin Contracts \(last updated v[^)]+\)/)[0]; + } catch (err) { + return null; + } +} + +for (const [ file, template ] of Object.entries({ + // SafeCast + 'utils/math/SafeCast.sol': './templates/SafeCast.js', + 'mocks/SafeCastMock.sol': './templates/SafeCastMock.js', + // EnumerableSet + 'utils/structs/EnumerableSet.sol': './templates/EnumerableSet.js', + 'mocks/EnumerableSetMock.sol': './templates/EnumerableSetMock.js', + // EnumerableMap + 'utils/structs/EnumerableMap.sol': './templates/EnumerableMap.js', + 'mocks/EnumerableMapMock.sol': './templates/EnumerableMapMock.js', + // Checkpoints + 'utils/Checkpoints.sol': './templates/Checkpoints.js', + 'mocks/CheckpointsMock.sol': './templates/CheckpointsMock.js', +})) { + const script = path.relative(path.join(__dirname, '../..'), __filename); + const input = path.join(path.dirname(script), template); + const output = `./contracts/${file}`; + const version = getVersion(output); + const content = format( + '// SPDX-License-Identifier: MIT', + ...(version ? [ version + ` (${file})` ] : []), + `// This file was procedurally generated from ${input}.`, + '', + require(template), + ); + + fs.writeFileSync(output, content); + cp.execFileSync('prettier', ['--write', output]); +} diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/Checkpoints.js b/lib/openzeppelin-contracts/scripts/generate/templates/Checkpoints.js new file mode 100644 index 0000000..858ddb4 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/Checkpoints.js @@ -0,0 +1,303 @@ +const format = require('../format-lines'); + +const VALUE_SIZES = [ 224, 160 ]; + +const header = `\ +pragma solidity ^0.8.0; + +import "./math/Math.sol"; +import "./math/SafeCast.sol"; + +/** + * @dev This library defines the \`History\` struct, for checkpointing values as they change at different points in + * time, and later looking up past values by block number. See {Votes} as an example. + * + * To create a history of checkpoints define a variable type \`Checkpoints.History\` in your contract, and store a new + * checkpoint for the current transaction block using the {push} function. + * + * _Available since v4.5._ + */ +`; + +const types = opts => `\ +struct ${opts.historyTypeName} { + ${opts.checkpointTypeName}[] ${opts.checkpointFieldName}; +} + +struct ${opts.checkpointTypeName} { + ${opts.keyTypeName} ${opts.keyFieldName}; + ${opts.valueTypeName} ${opts.valueFieldName}; +} +`; + +/* eslint-disable max-len */ +const operations = opts => `\ +/** + * @dev Pushes a (\`key\`, \`value\`) pair into a ${opts.historyTypeName} so that it is stored as the checkpoint. + * + * Returns previous value and new value. + */ +function push( + ${opts.historyTypeName} storage self, + ${opts.keyTypeName} key, + ${opts.valueTypeName} value +) internal returns (${opts.valueTypeName}, ${opts.valueTypeName}) { + return _insert(self.${opts.checkpointFieldName}, key, value); +} + +/** + * @dev Returns the value in the oldest checkpoint with key greater or equal than the search key, or zero if there is none. + */ +function lowerLookup(${opts.historyTypeName} storage self, ${opts.keyTypeName} key) internal view returns (${opts.valueTypeName}) { + uint256 len = self.${opts.checkpointFieldName}.length; + uint256 pos = _lowerBinaryLookup(self.${opts.checkpointFieldName}, key, 0, len); + return pos == len ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos).${opts.valueFieldName}; +} + +/** + * @dev Returns the value in the most recent checkpoint with key lower or equal than the search key. + */ +function upperLookup(${opts.historyTypeName} storage self, ${opts.keyTypeName} key) internal view returns (${opts.valueTypeName}) { + uint256 len = self.${opts.checkpointFieldName}.length; + uint256 pos = _upperBinaryLookup(self.${opts.checkpointFieldName}, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; +} +`; + +const legacyOperations = opts => `\ +/** + * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one + * before it is returned, or zero otherwise. Because the number returned corresponds to that at the end of the + * block, the requested block number must be in the past, excluding the current block. + */ +function getAtBlock(${opts.historyTypeName} storage self, uint256 blockNumber) internal view returns (uint256) { + require(blockNumber < block.number, "Checkpoints: block not yet mined"); + uint32 key = SafeCast.toUint32(blockNumber); + + uint256 len = self.${opts.checkpointFieldName}.length; + uint256 pos = _upperBinaryLookup(self.${opts.checkpointFieldName}, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; +} + +/** + * @dev Returns the value at a given block number. If a checkpoint is not available at that block, the closest one + * before it is returned, or zero otherwise. Similar to {upperLookup} but optimized for the case when the searched + * checkpoint is probably "recent", defined as being among the last sqrt(N) checkpoints where N is the number of + * checkpoints. + */ +function getAtProbablyRecentBlock(${opts.historyTypeName} storage self, uint256 blockNumber) internal view returns (uint256) { + require(blockNumber < block.number, "Checkpoints: block not yet mined"); + uint32 key = SafeCast.toUint32(blockNumber); + + uint256 len = self.${opts.checkpointFieldName}.length; + + uint256 low = 0; + uint256 high = len; + + if (len > 5) { + uint256 mid = len - Math.sqrt(len); + if (key < _unsafeAccess(self.${opts.checkpointFieldName}, mid)._blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + uint256 pos = _upperBinaryLookup(self.${opts.checkpointFieldName}, key, low, high); + + return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; +} + +/** + * @dev Pushes a value onto a History so that it is stored as the checkpoint for the current block. + * + * Returns previous value and new value. + */ +function push(${opts.historyTypeName} storage self, uint256 value) internal returns (uint256, uint256) { + return _insert(self.${opts.checkpointFieldName}, SafeCast.toUint32(block.number), SafeCast.toUint224(value)); +} + +/** + * @dev Pushes a value onto a History, by updating the latest value using binary operation \`op\`. The new value will + * be set to \`op(latest, delta)\`. + * + * Returns previous value and new value. + */ +function push( + ${opts.historyTypeName} storage self, + function(uint256, uint256) view returns (uint256) op, + uint256 delta +) internal returns (uint256, uint256) { + return push(self, op(latest(self), delta)); +} +`; + +const common = opts => `\ +/** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ +function latest(${opts.historyTypeName} storage self) internal view returns (${opts.valueTypeName}) { + uint256 pos = self.${opts.checkpointFieldName}.length; + return pos == 0 ? 0 : _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1).${opts.valueFieldName}; +} + +/** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ +function latestCheckpoint(${opts.historyTypeName} storage self) + internal + view + returns ( + bool exists, + ${opts.keyTypeName} ${opts.keyFieldName}, + ${opts.valueTypeName} ${opts.valueFieldName} + ) +{ + uint256 pos = self.${opts.checkpointFieldName}.length; + if (pos == 0) { + return (false, 0, 0); + } else { + ${opts.checkpointTypeName} memory ckpt = _unsafeAccess(self.${opts.checkpointFieldName}, pos - 1); + return (true, ckpt.${opts.keyFieldName}, ckpt.${opts.valueFieldName}); + } +} + +/** + * @dev Returns the number of checkpoint. + */ +function length(${opts.historyTypeName} storage self) internal view returns (uint256) { + return self.${opts.checkpointFieldName}.length; +} + +/** + * @dev Pushes a (\`key\`, \`value\`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ +function _insert( + ${opts.checkpointTypeName}[] storage self, + ${opts.keyTypeName} key, + ${opts.valueTypeName} value +) private returns (${opts.valueTypeName}, ${opts.valueTypeName}) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + ${opts.checkpointTypeName} memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + require(last.${opts.keyFieldName} <= key, "Checkpoint: decreasing keys"); + + // Update or push new checkpoint + if (last.${opts.keyFieldName} == key) { + _unsafeAccess(self, pos - 1).${opts.valueFieldName} = value; + } else { + self.push(${opts.checkpointTypeName}({${opts.keyFieldName}: key, ${opts.valueFieldName}: value})); + } + return (last.${opts.valueFieldName}, value); + } else { + self.push(${opts.checkpointTypeName}({${opts.keyFieldName}: key, ${opts.valueFieldName}: value})); + return (0, value); + } +} + +/** + * @dev Return the index of the oldest checkpoint whose key is greater than the search key, or \`high\` if there is none. + * \`low\` and \`high\` define a section where to do the search, with inclusive \`low\` and exclusive \`high\`. + * + * WARNING: \`high\` should not be greater than the array's length. + */ +function _upperBinaryLookup( + ${opts.checkpointTypeName}[] storage self, + ${opts.keyTypeName} key, + uint256 low, + uint256 high +) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid).${opts.keyFieldName} > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; +} + +/** + * @dev Return the index of the oldest checkpoint whose key is greater or equal than the search key, or \`high\` if there is none. + * \`low\` and \`high\` define a section where to do the search, with inclusive \`low\` and exclusive \`high\`. + * + * WARNING: \`high\` should not be greater than the array's length. + */ +function _lowerBinaryLookup( + ${opts.checkpointTypeName}[] storage self, + ${opts.keyTypeName} key, + uint256 low, + uint256 high +) private view returns (uint256) { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid).${opts.keyFieldName} < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; +} + +/** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ +function _unsafeAccess(${opts.checkpointTypeName}[] storage self, uint256 pos) + private + pure + returns (${opts.checkpointTypeName} storage result) +{ + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } +} +`; +/* eslint-enable max-len */ + +// OPTIONS +const defaultOpts = (size) => ({ + historyTypeName: `Trace${size}`, + checkpointTypeName: `Checkpoint${size}`, + checkpointFieldName: '_checkpoints', + keyTypeName: `uint${256 - size}`, + keyFieldName: '_key', + valueTypeName: `uint${size}`, + valueFieldName: '_value', +}); + +const OPTS = VALUE_SIZES.map(size => defaultOpts(size)); + +const LEGACY_OPTS = { + ...defaultOpts(224), + historyTypeName: 'History', + checkpointTypeName: 'Checkpoint', + keyFieldName: '_blockNumber', +}; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library Checkpoints {', + [ + // Legacy types & functions + types(LEGACY_OPTS), + legacyOperations(LEGACY_OPTS), + common(LEGACY_OPTS), + // New flavors + ...OPTS.flatMap(opts => [ + types(opts), + operations(opts), + common(opts), + ]), + ], + '}', +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/CheckpointsMock.js b/lib/openzeppelin-contracts/scripts/generate/templates/CheckpointsMock.js new file mode 100644 index 0000000..145f084 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/CheckpointsMock.js @@ -0,0 +1,80 @@ +const format = require('../format-lines'); + +const VALUE_SIZES = [ 224, 160 ]; + +const header = `\ +pragma solidity ^0.8.0; + +import "../utils/Checkpoints.sol"; +`; + +const legacy = () => `\ +contract CheckpointsMock { + using Checkpoints for Checkpoints.History; + + Checkpoints.History private _totalCheckpoints; + + function latest() public view returns (uint256) { + return _totalCheckpoints.latest(); + } + + function latestCheckpoint() public view returns (bool, uint256, uint256) { + return _totalCheckpoints.latestCheckpoint(); + } + + function length() public view returns (uint256) { + return _totalCheckpoints.length(); + } + + function push(uint256 value) public returns (uint256, uint256) { + return _totalCheckpoints.push(value); + } + + function getAtBlock(uint256 blockNumber) public view returns (uint256) { + return _totalCheckpoints.getAtBlock(blockNumber); + } + + function getAtProbablyRecentBlock(uint256 blockNumber) public view returns (uint256) { + return _totalCheckpoints.getAtProbablyRecentBlock(blockNumber); + } +} +`; + +const checkpoint = length => `\ +contract Checkpoints${length}Mock { + using Checkpoints for Checkpoints.Trace${length}; + + Checkpoints.Trace${length} private _totalCheckpoints; + + function latest() public view returns (uint${length}) { + return _totalCheckpoints.latest(); + } + + function latestCheckpoint() public view returns (bool, uint${256 - length}, uint${length}) { + return _totalCheckpoints.latestCheckpoint(); + } + + function length() public view returns (uint256) { + return _totalCheckpoints.length(); + } + + function push(uint${256 - length} key, uint${length} value) public returns (uint${length}, uint${length}) { + return _totalCheckpoints.push(key, value); + } + + function lowerLookup(uint${256 - length} key) public view returns (uint${length}) { + return _totalCheckpoints.lowerLookup(key); + } + + function upperLookup(uint${256 - length} key) public view returns (uint${length}) { + return _totalCheckpoints.upperLookup(key); + } +} +`; + +// GENERATE +module.exports = format( + header, + legacy(), + ...VALUE_SIZES.map(checkpoint), +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMap.js b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMap.js new file mode 100644 index 0000000..ca8e0e7 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMap.js @@ -0,0 +1,278 @@ +const format = require('../format-lines'); +const { fromBytes32, toBytes32 } = require('./conversion'); + +const TYPES = [ + { name: 'UintToUintMap', keyType: 'uint256', valueType: 'uint256' }, + { name: 'UintToAddressMap', keyType: 'uint256', valueType: 'address' }, + { name: 'AddressToUintMap', keyType: 'address', valueType: 'uint256' }, + { name: 'Bytes32ToUintMap', keyType: 'bytes32', valueType: 'uint256' }, +]; + +/* eslint-disable max-len */ +const header = `\ +pragma solidity ^0.8.0; + +import "./EnumerableSet.sol"; + +/** + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[\`mapping\`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * \`\`\` + * contract Example { + * // Add the library methods + * using EnumerableMap for EnumerableMap.UintToAddressMap; + * + * // Declare a set state variable + * EnumerableMap.UintToAddressMap private myMap; + * } + * \`\`\` + * + * The following map types are supported: + * + * - \`uint256 -> address\` (\`UintToAddressMap\`) since v3.0.0 + * - \`address -> uint256\` (\`AddressToUintMap\`) since v4.6.0 + * - \`bytes32 -> bytes32\` (\`Bytes32ToBytes32Map\`) since v4.6.0 + * - \`uint256 -> uint256\` (\`UintToUintMap\`) since v4.7.0 + * - \`bytes32 -> uint256\` (\`Bytes32ToUintMap\`) since v4.7.0 + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableMap. + * ==== + */ +`; +/* eslint-enable max-len */ + +const defaultMap = () => `\ +// To implement this library for multiple types with as little code +// repetition as possible, we write it in terms of a generic Map type with +// bytes32 keys and values. +// The Map implementation uses private functions, and user-facing +// implementations (such as Uint256ToAddressMap) are just wrappers around +// the underlying Map. +// This means that we can only create new EnumerableMaps for types that fit +// in bytes32. + +struct Bytes32ToBytes32Map { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 => bytes32) _values; +} + +/** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ +function set( + Bytes32ToBytes32Map storage map, + bytes32 key, + bytes32 value +) internal returns (bool) { + map._values[key] = value; + return map._keys.add(key); +} + +/** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ +function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) { + delete map._values[key]; + return map._keys.remove(key); +} + +/** + * @dev Returns true if the key is in the map. O(1). + */ +function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) { + return map._keys.contains(key); +} + +/** + * @dev Returns the number of key-value pairs in the map. O(1). + */ +function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) { + return map._keys.length(); +} + +/** + * @dev Returns the key-value pair stored at position \`index\` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - \`index\` must be strictly less than {length}. + */ +function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) { + bytes32 key = map._keys.at(index); + return (key, map._values[key]); +} + +/** + * @dev Tries to returns the value associated with \`key\`. O(1). + * Does not revert if \`key\` is not in the map. + */ +function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) { + bytes32 value = map._values[key]; + if (value == bytes32(0)) { + return (contains(map, key), bytes32(0)); + } else { + return (true, value); + } +} + +/** + * @dev Returns the value associated with \`key\`. O(1). + * + * Requirements: + * + * - \`key\` must be in the map. + */ +function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key"); + return value; +} + +/** + * @dev Same as {get}, with a custom error message when \`key\` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ +function get( + Bytes32ToBytes32Map storage map, + bytes32 key, + string memory errorMessage +) internal view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || contains(map, key), errorMessage); + return value; +} +`; + +const customMap = ({ name, keyType, valueType }) => `\ +// ${name} + +struct ${name} { + Bytes32ToBytes32Map _inner; +} + +/** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ +function set( + ${name} storage map, + ${keyType} key, + ${valueType} value +) internal returns (bool) { + return set(map._inner, ${toBytes32(keyType, 'key')}, ${toBytes32(valueType, 'value')}); +} + +/** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ +function remove(${name} storage map, ${keyType} key) internal returns (bool) { + return remove(map._inner, ${toBytes32(keyType, 'key')}); +} + +/** + * @dev Returns true if the key is in the map. O(1). + */ +function contains(${name} storage map, ${keyType} key) internal view returns (bool) { + return contains(map._inner, ${toBytes32(keyType, 'key')}); +} + +/** + * @dev Returns the number of elements in the map. O(1). + */ +function length(${name} storage map) internal view returns (uint256) { + return length(map._inner); +} + +/** + * @dev Returns the element stored at position \`index\` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - \`index\` must be strictly less than {length}. + */ +function at(${name} storage map, uint256 index) internal view returns (${keyType}, ${valueType}) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (${fromBytes32(keyType, 'key')}, ${fromBytes32(valueType, 'value')}); +} + +/** + * @dev Tries to returns the value associated with \`key\`. O(1). + * Does not revert if \`key\` is not in the map. + */ +function tryGet(${name} storage map, ${keyType} key) internal view returns (bool, ${valueType}) { + (bool success, bytes32 value) = tryGet(map._inner, ${toBytes32(keyType, 'key')}); + return (success, ${fromBytes32(valueType, 'value')}); +} + +/** + * @dev Returns the value associated with \`key\`. O(1). + * + * Requirements: + * + * - \`key\` must be in the map. + */ +function get(${name} storage map, ${keyType} key) internal view returns (${valueType}) { + return ${fromBytes32(valueType, `get(map._inner, ${toBytes32(keyType, 'key')})`)}; +} + +/** + * @dev Same as {get}, with a custom error message when \`key\` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ +function get( + ${name} storage map, + ${keyType} key, + string memory errorMessage +) internal view returns (${valueType}) { + return ${fromBytes32(valueType, `get(map._inner, ${toBytes32(keyType, 'key')}, errorMessage)`)}; +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library EnumerableMap {', + [ + 'using EnumerableSet for EnumerableSet.Bytes32Set;', + '', + defaultMap(), + TYPES.map(details => customMap(details).trimEnd()).join('\n\n'), + ], + '}', +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMapMock.js b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMapMock.js new file mode 100644 index 0000000..ff26a6a --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableMapMock.js @@ -0,0 +1,66 @@ +const format = require('../format-lines'); + +const TYPES = [ + { name: 'UintToAddressMap', keyType: 'uint256', valueType: 'address' }, + { name: 'AddressToUintMap', keyType: 'address', valueType: 'uint256' }, + { name: 'Bytes32ToBytes32Map', keyType: 'bytes32', valueType: 'bytes32' }, + { name: 'UintToUintMap', keyType: 'uint256', valueType: 'uint256' }, + { name: 'Bytes32ToUintMap', keyType: 'bytes32', valueType: 'uint256' }, +]; + +const header = `\ +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableMap.sol"; +`; + +const customSetMock = ({ name, keyType, valueType }) => `\ +// ${name} +contract ${name}Mock { + using EnumerableMap for EnumerableMap.${name}; + + event OperationResult(bool result); + + EnumerableMap.${name} private _map; + + function contains(${keyType} key) public view returns (bool) { + return _map.contains(key); + } + + function set(${keyType} key, ${valueType} value) public { + bool result = _map.set(key, value); + emit OperationResult(result); + } + + function remove(${keyType} key) public { + bool result = _map.remove(key); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _map.length(); + } + + function at(uint256 index) public view returns (${keyType} key, ${valueType} value) { + return _map.at(index); + } + + function tryGet(${keyType} key) public view returns (bool, ${valueType}) { + return _map.tryGet(key); + } + + function get(${keyType} key) public view returns (${valueType}) { + return _map.get(key); + } + + function getWithMessage(${keyType} key, string calldata errorMessage) public view returns (${valueType}) { + return _map.get(key, errorMessage); + } +} +`; + +// GENERATE +module.exports = format( + header, + ...TYPES.map(details => customSetMock(details)), +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSet.js b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSet.js new file mode 100644 index 0000000..22178ec --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSet.js @@ -0,0 +1,253 @@ +const format = require('../format-lines'); +const { fromBytes32, toBytes32 } = require('./conversion'); + +const TYPES = [ + { name: 'Bytes32Set', type: 'bytes32' }, + { name: 'AddressSet', type: 'address' }, + { name: 'UintSet', type: 'uint256' }, +]; + +/* eslint-disable max-len */ +const header = `\ +pragma solidity ^0.8.0; + +/** + * @dev Library for managing + * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive + * types. + * + * Sets have the following properties: + * + * - Elements are added, removed, and checked for existence in constant time + * (O(1)). + * - Elements are enumerated in O(n). No guarantees are made on the ordering. + * + * \`\`\` + * contract Example { + * // Add the library methods + * using EnumerableSet for EnumerableSet.AddressSet; + * + * // Declare a set state variable + * EnumerableSet.AddressSet private mySet; + * } + * \`\`\` + * + * As of v3.3.0, sets of type \`bytes32\` (\`Bytes32Set\`), \`address\` (\`AddressSet\`) + * and \`uint256\` (\`UintSet\`) are supported. + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableSet. + * ==== + */ +`; +/* eslint-enable max-len */ + +const defaultSet = () => `\ +// To implement this library for multiple types with as little code +// repetition as possible, we write it in terms of a generic Set type with +// bytes32 values. +// The Set implementation uses private functions, and user-facing +// implementations (such as AddressSet) are just wrappers around the +// underlying Set. +// This means that we can only create new EnumerableSets for types that fit +// in bytes32. + +struct Set { + // Storage of set values + bytes32[] _values; + // Position of the value in the \`values\` array, plus 1 because index 0 + // means a value is not in the set. + mapping(bytes32 => uint256) _indexes; +} + +/** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ +function _add(Set storage set, bytes32 value) private returns (bool) { + if (!_contains(set, value)) { + set._values.push(value); + // The value is stored at length-1, but we add 1 to all indexes + // and use 0 as a sentinel value + set._indexes[value] = set._values.length; + return true; + } else { + return false; + } +} + +/** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ +function _remove(Set storage set, bytes32 value) private returns (bool) { + // We read and store the value's index to prevent multiple reads from the same storage slot + uint256 valueIndex = set._indexes[value]; + + if (valueIndex != 0) { + // Equivalent to contains(set, value) + // To delete an element from the _values array in O(1), we swap the element to delete with the last one in + // the array, and then remove the last element (sometimes called as 'swap and pop'). + // This modifies the order of the array, as noted in {at}. + + uint256 toDeleteIndex = valueIndex - 1; + uint256 lastIndex = set._values.length - 1; + + if (lastIndex != toDeleteIndex) { + bytes32 lastValue = set._values[lastIndex]; + + // Move the last value to the index where the value to delete is + set._values[toDeleteIndex] = lastValue; + // Update the index for the moved value + set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex + } + + // Delete the slot where the moved value was stored + set._values.pop(); + + // Delete the index for the deleted slot + delete set._indexes[value]; + + return true; + } else { + return false; + } +} + +/** + * @dev Returns true if the value is in the set. O(1). + */ +function _contains(Set storage set, bytes32 value) private view returns (bool) { + return set._indexes[value] != 0; +} + +/** + * @dev Returns the number of values on the set. O(1). + */ +function _length(Set storage set) private view returns (uint256) { + return set._values.length; +} + +/** + * @dev Returns the value stored at position \`index\` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - \`index\` must be strictly less than {length}. + */ +function _at(Set storage set, uint256 index) private view returns (bytes32) { + return set._values[index]; +} + +/** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ +function _values(Set storage set) private view returns (bytes32[] memory) { + return set._values; +} +`; + +const customSet = ({ name, type }) => `\ +// ${name} + +struct ${name} { + Set _inner; +} + +/** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ +function add(${name} storage set, ${type} value) internal returns (bool) { + return _add(set._inner, ${toBytes32(type, 'value')}); +} + +/** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ +function remove(${name} storage set, ${type} value) internal returns (bool) { + return _remove(set._inner, ${toBytes32(type, 'value')}); +} + +/** + * @dev Returns true if the value is in the set. O(1). + */ +function contains(${name} storage set, ${type} value) internal view returns (bool) { + return _contains(set._inner, ${toBytes32(type, 'value')}); +} + +/** + * @dev Returns the number of values in the set. O(1). + */ +function length(${name} storage set) internal view returns (uint256) { + return _length(set._inner); +} + +/** + * @dev Returns the value stored at position \`index\` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - \`index\` must be strictly less than {length}. + */ +function at(${name} storage set, uint256 index) internal view returns (${type}) { + return ${fromBytes32(type, '_at(set._inner, index)')}; +} + +/** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ +function values(${name} storage set) internal view returns (${type}[] memory) { + bytes32[] memory store = _values(set._inner); + ${type}[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library EnumerableSet {', + [ + defaultSet(), + TYPES.map(details => customSet(details).trimEnd()).join('\n\n'), + ], + '}', +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSetMock.js b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSetMock.js new file mode 100644 index 0000000..fbc9b85 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/EnumerableSetMock.js @@ -0,0 +1,56 @@ +const format = require('../format-lines'); + +const TYPES = [ + { name: 'Bytes32Set', type: 'bytes32' }, + { name: 'AddressSet', type: 'address' }, + { name: 'UintSet', type: 'uint256' }, +]; + +const header = `\ +pragma solidity ^0.8.0; + +import "../utils/structs/EnumerableSet.sol"; +`; + +const customSetMock = ({ name, type }) => `\ +// ${name} +contract Enumerable${name}Mock { + using EnumerableSet for EnumerableSet.${name}; + + event OperationResult(bool result); + + EnumerableSet.${name} private _set; + + function contains(${type} value) public view returns (bool) { + return _set.contains(value); + } + + function add(${type} value) public { + bool result = _set.add(value); + emit OperationResult(result); + } + + function remove(${type} value) public { + bool result = _set.remove(value); + emit OperationResult(result); + } + + function length() public view returns (uint256) { + return _set.length(); + } + + function at(uint256 index) public view returns (${type}) { + return _set.at(index); + } + + function values() public view returns (${type}[] memory) { + return _set.values(); + } +} +`; + +// GENERATE +module.exports = format( + header, + ...TYPES.map(details => customSetMock(details)), +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/SafeCast.js b/lib/openzeppelin-contracts/scripts/generate/templates/SafeCast.js new file mode 100644 index 0000000..8cf1742 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/SafeCast.js @@ -0,0 +1,168 @@ +const assert = require('assert'); +const format = require('../format-lines'); +const { range } = require('../../helpers'); + +const LENGTHS = range(8, 256, 8).reverse(); // 248 → 8 (in steps of 8) + +// Returns the version of OpenZeppelin Contracts in which a particular function was introduced. +// This is used in the docs for each function. +const version = (selector, length) => { + switch (selector) { + case 'toUint(uint)': { + switch (length) { + case 8: + case 16: + case 32: + case 64: + case 128: + return '2.5'; + case 96: + case 224: + return '4.2'; + default: + assert(LENGTHS.includes(length)); + return '4.7'; + } + } + case 'toInt(int)': { + switch (length) { + case 8: + case 16: + case 32: + case 64: + case 128: + return '3.1'; + default: + assert(LENGTHS.includes(length)); + return '4.7'; + } + } + case 'toUint(int)': { + switch (length) { + case 256: + return '3.0'; + default: + assert(false); + return; + } + } + case 'toInt(uint)': { + switch (length) { + case 256: + return '3.0'; + default: + assert(false); + return; + } + } + default: + assert(false); + } +}; + +const header = `\ +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. \`SafeCast\` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on \`uint256\` and \`int256\` and then downcasting. + */ +`; + +const toUintDownCast = length => `\ +/** + * @dev Returns the downcasted uint${length} from uint256, reverting on + * overflow (when the input is greater than largest uint${length}). + * + * Counterpart to Solidity's \`uint${length}\` operator. + * + * Requirements: + * + * - input must fit into ${length} bits + * + * _Available since v${version('toUint(uint)', length)}._ + */ +function toUint${length}(uint256 value) internal pure returns (uint${length}) { + require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits"); + return uint${length}(value); +} +`; + +/* eslint-disable max-len */ +const toIntDownCast = length => `\ +/** + * @dev Returns the downcasted int${length} from int256, reverting on + * overflow (when the input is less than smallest int${length} or + * greater than largest int${length}). + * + * Counterpart to Solidity's \`int${length}\` operator. + * + * Requirements: + * + * - input must fit into ${length} bits + * + * _Available since v${version('toInt(int)', length)}._ + */ +function toInt${length}(int256 value) internal pure returns (int${length} downcasted) { + downcasted = int${length}(value); + require(downcasted == value, "SafeCast: value doesn't fit in ${length} bits"); +} +`; +/* eslint-enable max-len */ + +const toInt = length => `\ +/** + * @dev Converts an unsigned uint${length} into a signed int${length}. + * + * Requirements: + * + * - input must be less than or equal to maxInt${length}. + * + * _Available since v${version('toInt(uint)', length)}._ + */ +function toInt${length}(uint${length} value) internal pure returns (int${length}) { + // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive + require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}"); + return int${length}(value); +} +`; + +const toUint = length => `\ +/** + * @dev Converts a signed int${length} into an unsigned uint${length}. + * + * Requirements: + * + * - input must be greater than or equal to 0. + * + * _Available since v${version('toUint(int)', length)}._ + */ +function toUint${length}(int${length} value) internal pure returns (uint${length}) { + require(value >= 0, "SafeCast: value must be positive"); + return uint${length}(value); +} +`; + +// GENERATE +module.exports = format( + header.trimEnd(), + 'library SafeCast {', + [ + ...LENGTHS.map(toUintDownCast), + toUint(256), + ...LENGTHS.map(toIntDownCast), + toInt(256), + ], + '}', +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/SafeCastMock.js b/lib/openzeppelin-contracts/scripts/generate/templates/SafeCastMock.js new file mode 100644 index 0000000..196d9b4 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/SafeCastMock.js @@ -0,0 +1,50 @@ +const format = require('../format-lines'); +const { range } = require('../../helpers'); + +const LENGTHS = range(8, 256, 8).reverse(); // 248 → 8 (in steps of 8) + +const header = `\ +pragma solidity ^0.8.0; + +import "../utils/math/SafeCast.sol"; +`; + +const toInt = length => `\ +function toInt${length}(uint${length} a) public pure returns (int${length}) { + return a.toInt${length}(); +} +`; + +const toUint = length => `\ +function toUint${length}(int${length} a) public pure returns (uint${length}) { + return a.toUint${length}(); +} +`; + +const toIntDownCast = length => `\ +function toInt${length}(int256 a) public pure returns (int${length}) { + return a.toInt${length}(); +} +`; + +const toUintDownCast = length => `\ +function toUint${length}(uint256 a) public pure returns (uint${length}) { + return a.toUint${length}(); +} +`; + +// GENERATE +module.exports = format( + header, + 'contract SafeCastMock {', + [ + 'using SafeCast for uint256;', + 'using SafeCast for int256;', + '', + toUint(256), + ...LENGTHS.map(toUintDownCast), + toInt(256), + ...LENGTHS.map(toIntDownCast), + ].flatMap(fn => fn.split('\n')).slice(0, -1), + '}', +); diff --git a/lib/openzeppelin-contracts/scripts/generate/templates/conversion.js b/lib/openzeppelin-contracts/scripts/generate/templates/conversion.js new file mode 100644 index 0000000..5c26c68 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/generate/templates/conversion.js @@ -0,0 +1,30 @@ +function toBytes32 (type, value) { + switch (type) { + case 'bytes32': + return value; + case 'uint256': + return `bytes32(${value})`; + case 'address': + return `bytes32(uint256(uint160(${value})))`; + default: + throw new Error(`Conversion from ${type} to bytes32 not supported`); + } +} + +function fromBytes32 (type, value) { + switch (type) { + case 'bytes32': + return value; + case 'uint256': + return `uint256(${value})`; + case 'address': + return `address(uint160(uint256(${value})))`; + default: + throw new Error(`Conversion from bytes32 to ${type} not supported`); + } +} + +module.exports = { + toBytes32, + fromBytes32, +}; diff --git a/lib/openzeppelin-contracts/scripts/git-user-config.sh b/lib/openzeppelin-contracts/scripts/git-user-config.sh new file mode 100644 index 0000000..e7b81c3 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/git-user-config.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail -x + +git config user.name 'github-actions' +git config user.email '41898282+github-actions[bot]@users.noreply.github.com' diff --git a/lib/openzeppelin-contracts/scripts/helpers.js b/lib/openzeppelin-contracts/scripts/helpers.js new file mode 100644 index 0000000..cbd0e01 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/helpers.js @@ -0,0 +1,23 @@ +function chunk (array, size = 1) { + return Array.range(Math.ceil(array.length / size)).map(i => array.slice(i * size, i * size + size)); +} + +function range (start, stop = undefined, step = 1) { + if (!stop) { stop = start; start = 0; } + return start < stop ? Array(Math.ceil((stop - start) / step)).fill().map((_, i) => start + i * step) : []; +} + +function unique (array, op = x => x) { + return array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i); +} + +function zip (...args) { + return Array(Math.max(...args.map(arg => arg.length))).fill(null).map((_, i) => args.map(arg => arg[i])); +} + +module.exports = { + chunk, + range, + unique, + zip, +}; diff --git a/lib/openzeppelin-contracts/scripts/migrate-imports.js b/lib/openzeppelin-contracts/scripts/migrate-imports.js new file mode 100644 index 0000000..04ebce7 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/migrate-imports.js @@ -0,0 +1,180 @@ +#!/usr/bin/env node + +const { promises: fs } = require('fs'); +const path = require('path'); + +const pathUpdates = { + // 'access/AccessControl.sol': undefined, + // 'access/Ownable.sol': undefined, + 'access/TimelockController.sol': 'governance/TimelockController.sol', + 'cryptography/ECDSA.sol': 'utils/cryptography/ECDSA.sol', + 'cryptography/MerkleProof.sol': 'utils/cryptography/MerkleProof.sol', + 'drafts/EIP712.sol': 'utils/cryptography/EIP712.sol', + 'drafts/ERC20Permit.sol': 'token/ERC20/extensions/ERC20Permit.sol', + 'drafts/IERC20Permit.sol': 'token/ERC20/extensions/IERC20Permit.sol', + 'GSN/Context.sol': 'utils/Context.sol', + // 'GSN/GSNRecipientERC20Fee.sol': undefined, + // 'GSN/GSNRecipientSignature.sol': undefined, + // 'GSN/GSNRecipient.sol': undefined, + // 'GSN/IRelayHub.sol': undefined, + // 'GSN/IRelayRecipient.sol': undefined, + 'introspection/ERC165Checker.sol': 'utils/introspection/ERC165Checker.sol', + 'introspection/ERC165.sol': 'utils/introspection/ERC165.sol', + 'introspection/ERC1820Implementer.sol': 'utils/introspection/ERC1820Implementer.sol', + 'introspection/IERC165.sol': 'utils/introspection/IERC165.sol', + 'introspection/IERC1820Implementer.sol': 'utils/introspection/IERC1820Implementer.sol', + 'introspection/IERC1820Registry.sol': 'utils/introspection/IERC1820Registry.sol', + 'math/Math.sol': 'utils/math/Math.sol', + 'math/SafeMath.sol': 'utils/math/SafeMath.sol', + 'math/SignedSafeMath.sol': 'utils/math/SignedSafeMath.sol', + 'payment/escrow/ConditionalEscrow.sol': 'utils/escrow/ConditionalEscrow.sol', + 'payment/escrow/Escrow.sol': 'utils/escrow/Escrow.sol', + 'payment/escrow/RefundEscrow.sol': 'utils/escrow/RefundEscrow.sol', + 'payment/PaymentSplitter.sol': 'finance/PaymentSplitter.sol', + 'utils/PaymentSplitter.sol': 'finance/PaymentSplitter.sol', + 'payment/PullPayment.sol': 'security/PullPayment.sol', + 'presets/ERC1155PresetMinterPauser.sol': 'token/ERC1155/presets/ERC1155PresetMinterPauser.sol', + 'presets/ERC20PresetFixedSupply.sol': 'token/ERC20/presets/ERC20PresetFixedSupply.sol', + 'presets/ERC20PresetMinterPauser.sol': 'token/ERC20/presets/ERC20PresetMinterPauser.sol', + 'presets/ERC721PresetMinterPauserAutoId.sol': 'token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol', + 'presets/ERC777PresetFixedSupply.sol': 'token/ERC777/presets/ERC777PresetFixedSupply.sol', + 'proxy/BeaconProxy.sol': 'proxy/beacon/BeaconProxy.sol', + // 'proxy/Clones.sol': undefined, + 'proxy/IBeacon.sol': 'proxy/beacon/IBeacon.sol', + 'proxy/Initializable.sol': 'proxy/utils/Initializable.sol', + 'utils/Initializable.sol': 'proxy/utils/Initializable.sol', + 'proxy/ProxyAdmin.sol': 'proxy/transparent/ProxyAdmin.sol', + // 'proxy/Proxy.sol': undefined, + 'proxy/TransparentUpgradeableProxy.sol': 'proxy/transparent/TransparentUpgradeableProxy.sol', + 'proxy/UpgradeableBeacon.sol': 'proxy/beacon/UpgradeableBeacon.sol', + 'proxy/UpgradeableProxy.sol': 'proxy/ERC1967/ERC1967Proxy.sol', + 'token/ERC1155/ERC1155Burnable.sol': 'token/ERC1155/extensions/ERC1155Burnable.sol', + 'token/ERC1155/ERC1155Holder.sol': 'token/ERC1155/utils/ERC1155Holder.sol', + 'token/ERC1155/ERC1155Pausable.sol': 'token/ERC1155/extensions/ERC1155Pausable.sol', + 'token/ERC1155/ERC1155Receiver.sol': 'token/ERC1155/utils/ERC1155Receiver.sol', + // 'token/ERC1155/ERC1155.sol': undefined, + 'token/ERC1155/IERC1155MetadataURI.sol': 'token/ERC1155/extensions/IERC1155MetadataURI.sol', + // 'token/ERC1155/IERC1155Receiver.sol': undefined, + // 'token/ERC1155/IERC1155.sol': undefined, + 'token/ERC20/ERC20Burnable.sol': 'token/ERC20/extensions/ERC20Burnable.sol', + 'token/ERC20/ERC20Capped.sol': 'token/ERC20/extensions/ERC20Capped.sol', + 'token/ERC20/ERC20Pausable.sol': 'token/ERC20/extensions/ERC20Pausable.sol', + 'token/ERC20/ERC20Snapshot.sol': 'token/ERC20/extensions/ERC20Snapshot.sol', + // 'token/ERC20/ERC20.sol': undefined, + // 'token/ERC20/IERC20.sol': undefined, + 'token/ERC20/SafeERC20.sol': 'token/ERC20/utils/SafeERC20.sol', + 'token/ERC20/TokenTimelock.sol': 'token/ERC20/utils/TokenTimelock.sol', + 'token/ERC721/ERC721Burnable.sol': 'token/ERC721/extensions/ERC721Burnable.sol', + 'token/ERC721/ERC721Holder.sol': 'token/ERC721/utils/ERC721Holder.sol', + 'token/ERC721/ERC721Pausable.sol': 'token/ERC721/extensions/ERC721Pausable.sol', + // 'token/ERC721/ERC721.sol': undefined, + 'token/ERC721/IERC721Enumerable.sol': 'token/ERC721/extensions/IERC721Enumerable.sol', + 'token/ERC721/IERC721Metadata.sol': 'token/ERC721/extensions/IERC721Metadata.sol', + // 'token/ERC721/IERC721Receiver.sol': undefined, + // 'token/ERC721/IERC721.sol': undefined, + // 'token/ERC777/ERC777.sol': undefined, + // 'token/ERC777/IERC777Recipient.sol': undefined, + // 'token/ERC777/IERC777Sender.sol': undefined, + // 'token/ERC777/IERC777.sol': undefined, + // 'utils/Address.sol': undefined, + // 'utils/Arrays.sol': undefined, + // 'utils/Context.sol': undefined, + // 'utils/Counters.sol': undefined, + // 'utils/Create2.sol': undefined, + 'utils/EnumerableMap.sol': 'utils/structs/EnumerableMap.sol', + 'utils/EnumerableSet.sol': 'utils/structs/EnumerableSet.sol', + 'utils/Pausable.sol': 'security/Pausable.sol', + 'utils/ReentrancyGuard.sol': 'security/ReentrancyGuard.sol', + 'utils/SafeCast.sol': 'utils/math/SafeCast.sol', + // 'utils/Strings.sol': undefined, + 'utils/cryptography/draft-EIP712.sol': 'utils/cryptography/EIP712.sol', + 'token/ERC20/extensions/draft-ERC20Permit.sol': 'token/ERC20/extensions/ERC20Permit.sol', + 'token/ERC20/extensions/draft-IERC20Permit.sol': 'token/ERC20/extensions/IERC20Permit.sol', +}; + +async function main (paths = [ 'contracts' ]) { + const files = await listFilesRecursively(paths, /\.sol$/); + + const updatedFiles = []; + for (const file of files) { + if (await updateFile(file, updateImportPaths)) { + updatedFiles.push(file); + } + } + + if (updatedFiles.length > 0) { + console.log(`${updatedFiles.length} file(s) were updated`); + for (const c of updatedFiles) { + console.log('-', c); + } + } else { + console.log('No files were updated'); + } +} + +async function listFilesRecursively (paths, filter) { + const queue = paths; + const files = []; + + while (queue.length > 0) { + const top = queue.shift(); + const stat = await fs.stat(top); + if (stat.isFile()) { + if (top.match(filter)) { + files.push(top); + } + } else if (stat.isDirectory()) { + for (const name of await fs.readdir(top)) { + queue.push(path.join(top, name)); + } + } + } + + return files; +} + +async function updateFile (file, update) { + const content = await fs.readFile(file, 'utf8'); + const updatedContent = update(content); + if (updatedContent !== content) { + await fs.writeFile(file, updatedContent); + return true; + } else { + return false; + } +} + +function updateImportPaths (source) { + for (const [ oldPath, newPath ] of Object.entries(pathUpdates)) { + source = source.replace( + path.join('@openzeppelin/contracts', oldPath), + path.join('@openzeppelin/contracts', newPath), + ); + source = source.replace( + path.join('@openzeppelin/contracts-upgradeable', getUpgradeablePath(oldPath)), + path.join('@openzeppelin/contracts-upgradeable', getUpgradeablePath(newPath)), + ); + } + + return source; +} + +function getUpgradeablePath (file) { + const { dir, name, ext } = path.parse(file); + const upgradeableName = name + 'Upgradeable'; + return path.format({ dir, ext, name: upgradeableName }); +} + +module.exports = { + pathUpdates, + updateImportPaths, + getUpgradeablePath, +}; + +if (require.main === module) { + const args = process.argv.length > 2 ? process.argv.slice(2) : undefined; + main(args).catch(e => { + console.error(e); + process.exit(1); + }); +} diff --git a/lib/openzeppelin-contracts/scripts/prepack.sh b/lib/openzeppelin-contracts/scripts/prepack.sh new file mode 100644 index 0000000..6f1bd4c --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/prepack.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail +shopt -s globstar + +# cross platform `mkdir -p` +node -e 'fs.mkdirSync("build/contracts", { recursive: true })' + +cp artifacts/contracts/**/*.json build/contracts +rm build/contracts/*.dbg.json + +node scripts/remove-ignored-artifacts.js diff --git a/lib/openzeppelin-contracts/scripts/prepare-contracts-package.sh b/lib/openzeppelin-contracts/scripts/prepare-contracts-package.sh new file mode 100644 index 0000000..3f62fd4 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/prepare-contracts-package.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# cd to the root of the repo +cd "$(git rev-parse --show-toplevel)" + +# avoids re-compilation during publishing of both packages +if [[ ! -v ALREADY_COMPILED ]]; then + npm run clean + npm run prepare + npm run prepack +fi + +cp README.md contracts/ +mkdir contracts/build contracts/build/contracts +cp -r build/contracts/*.json contracts/build/contracts diff --git a/lib/openzeppelin-contracts/scripts/prepare-docs.sh b/lib/openzeppelin-contracts/scripts/prepare-docs.sh new file mode 100644 index 0000000..53a996b --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/prepare-docs.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -o errexit + +OUTDIR="$(node -p 'require("./docs/config.js").outputDir')" + +if [ ! -d node_modules ]; then + npm ci +fi + +rm -rf "$OUTDIR" + +hardhat docgen + +node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc" diff --git a/lib/openzeppelin-contracts/scripts/prepare.sh b/lib/openzeppelin-contracts/scripts/prepare.sh new file mode 100644 index 0000000..7014a70 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/prepare.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [ "${SKIP_COMPILE:-}" == true ]; then + exit +fi + +npm run clean +env COMPILE_MODE=production npm run compile diff --git a/lib/openzeppelin-contracts/scripts/release/release.sh b/lib/openzeppelin-contracts/scripts/release/release.sh new file mode 100644 index 0000000..af5091b --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/release/release.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash + +# Exit script as soon as a command fails. +set -o errexit + +# Default the prerelease version suffix to rc +: ${PRERELEASE_SUFFIX:=rc} + +log() { + # Print to stderr to prevent this from being 'returned' + echo "$@" > /dev/stderr +} + +current_version() { + echo "v$(node --print --eval "require('./package.json').version")" +} + +current_release_branch() { + v="$(current_version)" # 3.3.0-rc.0 + vf="${v%-"$PRERELEASE_SUFFIX".*}" # 3.3.0 + r="${vf%.*}" # 3.3 + echo "release-$r" +} + +assert_current_branch() { + current_branch="$(git symbolic-ref --short HEAD)" + expected_branch="$1" + if [[ "$current_branch" != "$expected_branch" ]]; then + log "Current branch '$current_branch' is not '$expected_branch'" + exit 1 + fi +} + +push_release_branch_and_tag() { + git push upstream "$(current_release_branch)" "$(current_version)" +} + +publish() { + dist_tag="$1" + + log "Publishing @openzeppelin/contracts on npm" + cd contracts + npm publish --tag "$dist_tag" --otp "$(prompt_otp)" + cd .. + + if [[ "$dist_tag" == "latest" ]]; then + npm dist-tag rm --otp "$(prompt_otp)" @openzeppelin/contracts next + fi +} + +push_and_publish() { + dist_tag="$1" + + log "Pushing release branch and tags to upstream" + push_release_branch_and_tag + + publish "$dist_tag" +} + +prompt_otp() { + log -n "Enter npm 2FA token: " + read -r otp + echo "$otp" +} + +environment_check() { + if ! git remote get-url upstream &> /dev/null; then + log "No 'upstream' remote found" + exit 1 + fi + + if npm whoami &> /dev/null; then + log "Will publish as '$(npm whoami)'" + else + log "Not logged in into npm, run 'npm login' first" + exit 1 + fi +} + +environment_check + +if [[ "$*" == "push" ]]; then + push_and_publish next + +elif [[ "$*" == "start minor" ]]; then + log "Creating new minor pre-release" + + assert_current_branch master + + # Create temporary release branch + git checkout -b release-temp + + # This bumps minor and adds prerelease suffix, commits the changes, and tags the commit + npm version preminor --preid="$PRERELEASE_SUFFIX" + + # Rename the release branch + git branch --move "$(current_release_branch)" + + push_and_publish next + +elif [[ "$*" == "start major" ]]; then + log "Creating new major pre-release" + + assert_current_branch master + + # Create temporary release branch + git checkout -b release-temp + + # This bumps major and adds prerelease suffix, commits the changes, and tags the commit + npm version premajor --preid="$PRERELEASE_SUFFIX" + + # Rename the release branch + git branch --move "$(current_release_branch)" + + push_and_publish next + +elif [[ "$*" == "rc" ]]; then + log "Bumping pre-release" + + assert_current_branch "$(current_release_branch)" + + # Bumps prerelease number, commits and tags + npm version prerelease + + push_and_publish next + +elif [[ "$*" == "final" ]]; then + # Update changelog release date, remove prerelease suffix, tag, push to git, publish in npm, remove next dist-tag + log "Creating final release" + + assert_current_branch "$(current_release_branch)" + + # This will remove the prerelease suffix from the version + npm version patch + + push_release_branch_and_tag + + push_and_publish latest + + log "Remember to merge the release branch into master and push upstream" + +else + log "Unknown command: '$*'" + exit 1 +fi diff --git a/lib/openzeppelin-contracts/scripts/release/synchronize-versions.js b/lib/openzeppelin-contracts/scripts/release/synchronize-versions.js new file mode 100644 index 0000000..15915a1 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/release/synchronize-versions.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +// Synchronizes the version in contracts/package.json with the one in package.json. +// This is run automatically when npm version is run. + +const fs = require('fs'); +const cp = require('child_process'); + +setVersion('contracts/package.json'); + +function setVersion (file) { + const json = JSON.parse(fs.readFileSync(file)); + json.version = process.env.npm_package_version; + fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n'); + cp.execFileSync('git', ['add', file]); +} diff --git a/lib/openzeppelin-contracts/scripts/release/update-changelog-release-date.js b/lib/openzeppelin-contracts/scripts/release/update-changelog-release-date.js new file mode 100644 index 0000000..c368eb7 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/release/update-changelog-release-date.js @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +// Sets the release date of the current release in the changelog. +// This is run automatically when npm version is run. + +const fs = require('fs'); +const cp = require('child_process'); + +const suffix = process.env.PRERELEASE_SUFFIX || 'rc'; + +const changelog = fs.readFileSync('CHANGELOG.md', 'utf8'); + +// The changelog entry to be updated looks like this: +// ## Unreleased +// We need to add the version and release date in a YYYY-MM-DD format, so that it looks like this: +// ## 2.5.3 (2019-04-25) + +const pkg = require('../../package.json'); +const version = pkg.version.replace(new RegExp('-' + suffix + '\\..*'), ''); + +const header = new RegExp(`^## (Unreleased|${version})$`, 'm'); + +if (!header.test(changelog)) { + console.error('Missing changelog entry'); + process.exit(1); +} + +const newHeader = pkg.version.indexOf(suffix) === -1 + ? `## ${version} (${new Date().toISOString().split('T')[0]})` + : `## ${version}`; + +fs.writeFileSync('CHANGELOG.md', changelog.replace(header, newHeader)); + +cp.execSync('git add CHANGELOG.md', { stdio: 'inherit' }); diff --git a/lib/openzeppelin-contracts/scripts/release/update-comment.js b/lib/openzeppelin-contracts/scripts/release/update-comment.js new file mode 100644 index 0000000..7baf8c2 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/release/update-comment.js @@ -0,0 +1,36 @@ +#!/usr/bin/env node +const fs = require('fs'); +const proc = require('child_process'); +const semver = require('semver'); +const run = (cmd, ...args) => proc.execFileSync(cmd, args, { encoding: 'utf8' }).trim(); + +const gitStatus = run('git', 'status', '--porcelain', '-uno', 'contracts/**/*.sol'); +if (gitStatus.length > 0) { + console.error('Contracts directory is not clean'); + process.exit(1); +} + +const { version } = require('../../package.json'); + +// Get latest tag according to semver. +const [ tag ] = run('git', 'tag') + .split(/\r?\n/) + .filter(semver.coerce) // check version can be processed + .filter(v => semver.lt(semver.coerce(v), version)) // only consider older tags, ignore current prereleases + .sort(semver.rcompare); + +// Ordering tag → HEAD is important here. +const files = run('git', 'diff', tag, 'HEAD', '--name-only', 'contracts/**/*.sol') + .split(/\r?\n/) + .filter(file => file && !file.match(/mock/i) && fs.existsSync(file)); + +for (const file of files) { + const current = fs.readFileSync(file, 'utf8'); + const updated = current.replace( + /(\/\/ SPDX-License-Identifier:.*)$(\n\/\/ OpenZeppelin Contracts .*$)?/m, + `$1\n// OpenZeppelin Contracts (last updated v${version}) (${file.replace('contracts/', '')})`, + ); + fs.writeFileSync(file, updated); +} + +run('git', 'add', '--update', 'contracts'); diff --git a/lib/openzeppelin-contracts/scripts/release/version.sh b/lib/openzeppelin-contracts/scripts/release/version.sh new file mode 100644 index 0000000..73d3026 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/release/version.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -o errexit + +scripts/release/update-changelog-release-date.js +scripts/release/synchronize-versions.js +scripts/release/update-comment.js + +oz-docs update-version diff --git a/lib/openzeppelin-contracts/scripts/remove-ignored-artifacts.js b/lib/openzeppelin-contracts/scripts/remove-ignored-artifacts.js new file mode 100644 index 0000000..2ef2788 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/remove-ignored-artifacts.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node + +// This script removes the build artifacts of ignored contracts. + +const fs = require('fs'); +const path = require('path'); +const match = require('micromatch'); + +function readJSON (path) { + return JSON.parse(fs.readFileSync(path)); +} + +const pkgFiles = readJSON('package.json').files; + +// Get only negated patterns. +const ignorePatterns = pkgFiles + .filter(pat => pat.startsWith('!')) +// Remove the negation part. Makes micromatch usage more intuitive. + .map(pat => pat.slice(1)); + +const ignorePatternsSubtrees = ignorePatterns +// Add **/* to ignore all files contained in the directories. + .concat(ignorePatterns.map(pat => path.join(pat, '**/*'))) + .map(p => p.replace(/^\//, '')); + +const artifactsDir = 'build/contracts'; +const buildinfo = 'artifacts/build-info'; +const filenames = fs.readdirSync(buildinfo); + +let n = 0; + +for (const filename of filenames) { + const solcOutput = readJSON(path.join(buildinfo, filename)).output; + for (const sourcePath in solcOutput.contracts) { + const ignore = match.any(sourcePath, ignorePatternsSubtrees); + if (ignore) { + for (const contract in solcOutput.contracts[sourcePath]) { + fs.unlinkSync(path.join(artifactsDir, contract + '.json')); + n += 1; + } + } + } +} + +console.error(`Removed ${n} mock artifacts`); diff --git a/lib/openzeppelin-contracts/scripts/update-docs-branch.js b/lib/openzeppelin-contracts/scripts/update-docs-branch.js new file mode 100644 index 0000000..82bb7e0 --- /dev/null +++ b/lib/openzeppelin-contracts/scripts/update-docs-branch.js @@ -0,0 +1,55 @@ +const proc = require('child_process'); +const read = cmd => proc.execSync(cmd, { encoding: 'utf8' }).trim(); +const run = cmd => { proc.execSync(cmd, { stdio: 'inherit' }); }; +const tryRead = cmd => { try { return read(cmd); } catch (e) { return undefined; } }; + +const releaseBranchRegex = /^release-v(?(?\d+)\.(?\d+)(?:\.(?\d+))?)$/; + +const currentBranch = read('git rev-parse --abbrev-ref HEAD'); +const match = currentBranch.match(releaseBranchRegex); + +if (!match) { + console.error('Not currently on a release branch'); + process.exit(1); +} + +if (/-.*$/.test(require('../package.json').version)) { + console.error('Refusing to update docs: prerelease detected'); + process.exit(0); +} + +const current = match.groups; +const docsBranch = `docs-v${current.major}.x`; + +// Fetch remotes and find the docs branch if it exists +run('git fetch --all --no-tags'); +const matchingDocsBranches = tryRead(`git rev-parse --glob='*/${docsBranch}'`); + +if (!matchingDocsBranches) { + // Create the branch + run(`git checkout --orphan ${docsBranch}`); +} else { + const [publishedRef, ...others] = new Set(matchingDocsBranches.split('\n')); + if (others.length > 0) { + console.error( + `Found conflicting ${docsBranch} branches.\n` + + 'Either local branch is outdated or there are multiple matching remote branches.', + ); + process.exit(1); + } + const publishedVersion = JSON.parse(read(`git show ${publishedRef}:package.json`)).version; + const publishedMinor = publishedVersion.match(/\d+\.(?\d+)\.\d+/).groups.minor; + if (current.minor < publishedMinor) { + console.error('Refusing to update docs: newer version is published'); + process.exit(0); + } + + run('git checkout --quiet --detach'); + run(`git reset --soft ${publishedRef}`); + run(`git checkout ${docsBranch}`); +} + +run('npm run prepare-docs'); +run('git add -f docs'); // --force needed because generated docs files are gitignored +run('git commit -m "Update docs"'); +run(`git checkout ${currentBranch}`); diff --git a/lib/openzeppelin-contracts/slither.config.json b/lib/openzeppelin-contracts/slither.config.json new file mode 100644 index 0000000..e52e3f5 --- /dev/null +++ b/lib/openzeppelin-contracts/slither.config.json @@ -0,0 +1,4 @@ +{ + "detectors_to_run": "reentrancy-eth,reentrancy-no-eth,reentrancy-unlimited-gas", + "filter_paths": "contracts/mocks" +} \ No newline at end of file diff --git a/lib/openzeppelin-contracts/test/TESTING.md b/lib/openzeppelin-contracts/test/TESTING.md new file mode 100644 index 0000000..a5ee932 --- /dev/null +++ b/lib/openzeppelin-contracts/test/TESTING.md @@ -0,0 +1,3 @@ +## Testing + +Unit test are critical to OpenZeppelin Contracts. They help ensure code quality and mitigate against security vulnerabilities. The directory structure within the `/test` directory corresponds to the `/contracts` directory. diff --git a/lib/openzeppelin-contracts/test/access/AccessControl.behavior.js b/lib/openzeppelin-contracts/test/access/AccessControl.behavior.js new file mode 100644 index 0000000..53dfa3d --- /dev/null +++ b/lib/openzeppelin-contracts/test/access/AccessControl.behavior.js @@ -0,0 +1,216 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior'); + +const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; +const ROLE = web3.utils.soliditySha3('ROLE'); +const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE'); + +function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) { + shouldSupportInterfaces(['AccessControl']); + + describe('default admin', function () { + it('deployer has default admin role', async function () { + expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true); + }); + + it('other roles\'s admin is the default admin role', async function () { + expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + }); + + it('default admin role\'s admin is itself', async function () { + expect(await this.accessControl.getRoleAdmin(DEFAULT_ADMIN_ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + }); + }); + + describe('granting', function () { + beforeEach(async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + }); + + it('non-admin cannot grant role to other accounts', async function () { + await expectRevert( + this.accessControl.grantRole(ROLE, authorized, { from: other }), + `${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`, + ); + }); + + it('accounts can be granted a role multiple times', async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + expectEvent.notEmitted(receipt, 'RoleGranted'); + }); + }); + + describe('revoking', function () { + it('roles that are not had can be revoked', async function () { + expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false); + + const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin }); + expectEvent.notEmitted(receipt, 'RoleRevoked'); + }); + + context('with granted role', function () { + beforeEach(async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + }); + + it('admin can revoke role', async function () { + const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin }); + expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: admin }); + + expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false); + }); + + it('non-admin cannot revoke role', async function () { + await expectRevert( + this.accessControl.revokeRole(ROLE, authorized, { from: other }), + `${errorPrefix}: account ${other.toLowerCase()} is missing role ${DEFAULT_ADMIN_ROLE}`, + ); + }); + + it('a role can be revoked multiple times', async function () { + await this.accessControl.revokeRole(ROLE, authorized, { from: admin }); + + const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: admin }); + expectEvent.notEmitted(receipt, 'RoleRevoked'); + }); + }); + }); + + describe('renouncing', function () { + it('roles that are not had can be renounced', async function () { + const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized }); + expectEvent.notEmitted(receipt, 'RoleRevoked'); + }); + + context('with granted role', function () { + beforeEach(async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + }); + + it('bearer can renounce role', async function () { + const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized }); + expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: authorized }); + + expect(await this.accessControl.hasRole(ROLE, authorized)).to.equal(false); + }); + + it('only the sender can renounce their roles', async function () { + await expectRevert( + this.accessControl.renounceRole(ROLE, authorized, { from: admin }), + `${errorPrefix}: can only renounce roles for self`, + ); + }); + + it('a role can be renounced multiple times', async function () { + await this.accessControl.renounceRole(ROLE, authorized, { from: authorized }); + + const receipt = await this.accessControl.renounceRole(ROLE, authorized, { from: authorized }); + expectEvent.notEmitted(receipt, 'RoleRevoked'); + }); + }); + }); + + describe('setting role admin', function () { + beforeEach(async function () { + const receipt = await this.accessControl.setRoleAdmin(ROLE, OTHER_ROLE); + expectEvent(receipt, 'RoleAdminChanged', { + role: ROLE, + previousAdminRole: DEFAULT_ADMIN_ROLE, + newAdminRole: OTHER_ROLE, + }); + + await this.accessControl.grantRole(OTHER_ROLE, otherAdmin, { from: admin }); + }); + + it('a role\'s admin role can be changed', async function () { + expect(await this.accessControl.getRoleAdmin(ROLE)).to.equal(OTHER_ROLE); + }); + + it('the new admin can grant roles', async function () { + const receipt = await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin }); + expectEvent(receipt, 'RoleGranted', { account: authorized, role: ROLE, sender: otherAdmin }); + }); + + it('the new admin can revoke roles', async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: otherAdmin }); + const receipt = await this.accessControl.revokeRole(ROLE, authorized, { from: otherAdmin }); + expectEvent(receipt, 'RoleRevoked', { account: authorized, role: ROLE, sender: otherAdmin }); + }); + + it('a role\'s previous admins no longer grant roles', async function () { + await expectRevert( + this.accessControl.grantRole(ROLE, authorized, { from: admin }), + `${errorPrefix}: account ${admin.toLowerCase()} is missing role ${OTHER_ROLE}`, + ); + }); + + it('a role\'s previous admins no longer revoke roles', async function () { + await expectRevert( + this.accessControl.revokeRole(ROLE, authorized, { from: admin }), + `${errorPrefix}: account ${admin.toLowerCase()} is missing role ${OTHER_ROLE}`, + ); + }); + }); + + describe('onlyRole modifier', function () { + beforeEach(async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + }); + + it('do not revert if sender has role', async function () { + await this.accessControl.senderProtected(ROLE, { from: authorized }); + }); + + it('revert if sender doesn\'t have role #1', async function () { + await expectRevert( + this.accessControl.senderProtected(ROLE, { from: other }), + `${errorPrefix}: account ${other.toLowerCase()} is missing role ${ROLE}`, + ); + }); + + it('revert if sender doesn\'t have role #2', async function () { + await expectRevert( + this.accessControl.senderProtected(OTHER_ROLE, { from: authorized }), + `${errorPrefix}: account ${authorized.toLowerCase()} is missing role ${OTHER_ROLE}`, + ); + }); + }); +} + +function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) { + shouldSupportInterfaces(['AccessControlEnumerable']); + + describe('enumerating', function () { + it('role bearers can be enumerated', async function () { + await this.accessControl.grantRole(ROLE, authorized, { from: admin }); + await this.accessControl.grantRole(ROLE, other, { from: admin }); + await this.accessControl.grantRole(ROLE, otherAuthorized, { from: admin }); + await this.accessControl.revokeRole(ROLE, other, { from: admin }); + + const memberCount = await this.accessControl.getRoleMemberCount(ROLE); + expect(memberCount).to.bignumber.equal('2'); + + const bearers = []; + for (let i = 0; i < memberCount; ++i) { + bearers.push(await this.accessControl.getRoleMember(ROLE, i)); + } + + expect(bearers).to.have.members([authorized, otherAuthorized]); + }); + it('role enumeration should be in sync after renounceRole call', async function () { + expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0'); + await this.accessControl.grantRole(ROLE, admin, { from: admin }); + expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('1'); + await this.accessControl.renounceRole(ROLE, admin, { from: admin }); + expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0'); + }); + }); +} + +module.exports = { + shouldBehaveLikeAccessControl, + shouldBehaveLikeAccessControlEnumerable, +}; diff --git a/lib/openzeppelin-contracts/test/access/AccessControl.test.js b/lib/openzeppelin-contracts/test/access/AccessControl.test.js new file mode 100644 index 0000000..cd9912a --- /dev/null +++ b/lib/openzeppelin-contracts/test/access/AccessControl.test.js @@ -0,0 +1,13 @@ +const { + shouldBehaveLikeAccessControl, +} = require('./AccessControl.behavior.js'); + +const AccessControlMock = artifacts.require('AccessControlMock'); + +contract('AccessControl', function (accounts) { + beforeEach(async function () { + this.accessControl = await AccessControlMock.new({ from: accounts[0] }); + }); + + shouldBehaveLikeAccessControl('AccessControl', ...accounts); +}); diff --git a/lib/openzeppelin-contracts/test/access/AccessControlCrossChain.test.js b/lib/openzeppelin-contracts/test/access/AccessControlCrossChain.test.js new file mode 100644 index 0000000..cb4f362 --- /dev/null +++ b/lib/openzeppelin-contracts/test/access/AccessControlCrossChain.test.js @@ -0,0 +1,59 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); +const { BridgeHelper } = require('../helpers/crosschain'); + +const { + shouldBehaveLikeAccessControl, +} = require('./AccessControl.behavior.js'); + +const crossChainRoleAlias = (role) => web3.utils.leftPad( + web3.utils.toHex(web3.utils.toBN(role).xor(web3.utils.toBN(web3.utils.soliditySha3('CROSSCHAIN_ALIAS')))), + 64, +); + +const AccessControlCrossChainMock = artifacts.require('AccessControlCrossChainMock'); + +const ROLE = web3.utils.soliditySha3('ROLE'); + +contract('AccessControl', function (accounts) { + before(async function () { + this.bridge = await BridgeHelper.deploy(); + }); + + beforeEach(async function () { + this.accessControl = await AccessControlCrossChainMock.new({ from: accounts[0] }); + }); + + shouldBehaveLikeAccessControl('AccessControl', ...accounts); + + describe('CrossChain enabled', function () { + beforeEach(async function () { + await this.accessControl.grantRole(ROLE, accounts[0], { from: accounts[0] }); + await this.accessControl.grantRole(crossChainRoleAlias(ROLE), accounts[1], { from: accounts[0] }); + }); + + it('check alliassing', async function () { + expect(await this.accessControl.crossChainRoleAlias(ROLE)).to.be.bignumber.equal(crossChainRoleAlias(ROLE)); + }); + + it('Crosschain calls not authorized to non-aliased addresses', async function () { + await expectRevert( + this.bridge.call( + accounts[0], + this.accessControl, + 'senderProtected', + [ ROLE ], + ), + `AccessControl: account ${accounts[0].toLowerCase()} is missing role ${crossChainRoleAlias(ROLE)}`, + ); + }); + + it('Crosschain calls not authorized to non-aliased addresses', async function () { + await this.bridge.call( + accounts[1], + this.accessControl, + 'senderProtected', + [ ROLE ], + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/access/AccessControlEnumerable.test.js b/lib/openzeppelin-contracts/test/access/AccessControlEnumerable.test.js new file mode 100644 index 0000000..fa5b546 --- /dev/null +++ b/lib/openzeppelin-contracts/test/access/AccessControlEnumerable.test.js @@ -0,0 +1,15 @@ +const { + shouldBehaveLikeAccessControl, + shouldBehaveLikeAccessControlEnumerable, +} = require('./AccessControl.behavior.js'); + +const AccessControlMock = artifacts.require('AccessControlEnumerableMock'); + +contract('AccessControl', function (accounts) { + beforeEach(async function () { + this.accessControl = await AccessControlMock.new({ from: accounts[0] }); + }); + + shouldBehaveLikeAccessControl('AccessControl', ...accounts); + shouldBehaveLikeAccessControlEnumerable('AccessControl', ...accounts); +}); diff --git a/lib/openzeppelin-contracts/test/access/Ownable.test.js b/lib/openzeppelin-contracts/test/access/Ownable.test.js new file mode 100644 index 0000000..894e77c --- /dev/null +++ b/lib/openzeppelin-contracts/test/access/Ownable.test.js @@ -0,0 +1,57 @@ +const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const Ownable = artifacts.require('OwnableMock'); + +contract('Ownable', function (accounts) { + const [ owner, other ] = accounts; + + beforeEach(async function () { + this.ownable = await Ownable.new({ from: owner }); + }); + + it('has an owner', async function () { + expect(await this.ownable.owner()).to.equal(owner); + }); + + describe('transfer ownership', function () { + it('changes owner after transfer', async function () { + const receipt = await this.ownable.transferOwnership(other, { from: owner }); + expectEvent(receipt, 'OwnershipTransferred'); + + expect(await this.ownable.owner()).to.equal(other); + }); + + it('prevents non-owners from transferring', async function () { + await expectRevert( + this.ownable.transferOwnership(other, { from: other }), + 'Ownable: caller is not the owner', + ); + }); + + it('guards ownership against stuck state', async function () { + await expectRevert( + this.ownable.transferOwnership(ZERO_ADDRESS, { from: owner }), + 'Ownable: new owner is the zero address', + ); + }); + }); + + describe('renounce ownership', function () { + it('loses owner after renouncement', async function () { + const receipt = await this.ownable.renounceOwnership({ from: owner }); + expectEvent(receipt, 'OwnershipTransferred'); + + expect(await this.ownable.owner()).to.equal(ZERO_ADDRESS); + }); + + it('prevents non-owners from renouncement', async function () { + await expectRevert( + this.ownable.renounceOwnership({ from: other }), + 'Ownable: caller is not the owner', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/access/Ownable2Step.test.js b/lib/openzeppelin-contracts/test/access/Ownable2Step.test.js new file mode 100644 index 0000000..0aeb224 --- /dev/null +++ b/lib/openzeppelin-contracts/test/access/Ownable2Step.test.js @@ -0,0 +1,57 @@ +const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; +const { expect } = require('chai'); + +const Ownable2Step = artifacts.require('Ownable2StepMock'); + +contract('Ownable2Step', function (accounts) { + const [owner, accountA, accountB] = accounts; + + beforeEach(async function () { + this.ownable2Step = await Ownable2Step.new({ from: owner }); + }); + + describe('transfer ownership', function () { + it('starting a transfer does not change owner', async function () { + const receipt = await this.ownable2Step.transferOwnership(accountA, { from: owner }); + expectEvent(receipt, 'OwnershipTransferStarted', { previousOwner: owner, newOwner: accountA }); + expect(await this.ownable2Step.owner()).to.equal(owner); + expect(await this.ownable2Step.pendingOwner()).to.equal(accountA); + }); + + it('changes owner after transfer', async function () { + await this.ownable2Step.transferOwnership(accountA, { from: owner }); + const receipt = await this.ownable2Step.acceptOwnership({ from: accountA }); + expectEvent(receipt, 'OwnershipTransferred', { previousOwner: owner, newOwner: accountA }); + expect(await this.ownable2Step.owner()).to.equal(accountA); + expect(await this.ownable2Step.pendingOwner()).to.not.equal(accountA); + }); + + it('changes owner after renouncing ownership', async function () { + await this.ownable2Step.renounceOwnership({ from: owner }); + // If renounceOwnership is removed from parent an alternative is needed ... + // without it is difficult to cleanly renounce with the two step process + // see: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3620#discussion_r957930388 + expect(await this.ownable2Step.owner()).to.equal(ZERO_ADDRESS); + }); + + it('pending owner resets after renouncing ownership', async function () { + await this.ownable2Step.transferOwnership(accountA, { from: owner }); + expect(await this.ownable2Step.pendingOwner()).to.equal(accountA); + await this.ownable2Step.renounceOwnership({ from: owner }); + expect(await this.ownable2Step.pendingOwner()).to.equal(ZERO_ADDRESS); + await expectRevert( + this.ownable2Step.acceptOwnership({ from: accountA }), + 'Ownable2Step: caller is not the new owner', + ); + }); + + it('guards transfer against invalid user', async function () { + await this.ownable2Step.transferOwnership(accountA, { from: owner }); + await expectRevert( + this.ownable2Step.acceptOwnership({ from: accountB }), + 'Ownable2Step: caller is not the new owner', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/crosschain/CrossChainEnabled.test.js b/lib/openzeppelin-contracts/test/crosschain/CrossChainEnabled.test.js new file mode 100644 index 0000000..bff9558 --- /dev/null +++ b/lib/openzeppelin-contracts/test/crosschain/CrossChainEnabled.test.js @@ -0,0 +1,88 @@ +const { BridgeHelper } = require('../helpers/crosschain'); +const { expectRevertCustomError } = require('../helpers/customError'); + +function randomAddress () { + return web3.utils.toChecksumAddress(web3.utils.randomHex(20)); +} + +const CrossChainEnabledAMBMock = artifacts.require('CrossChainEnabledAMBMock'); +const CrossChainEnabledArbitrumL1Mock = artifacts.require('CrossChainEnabledArbitrumL1Mock'); +const CrossChainEnabledArbitrumL2Mock = artifacts.require('CrossChainEnabledArbitrumL2Mock'); +const CrossChainEnabledOptimismMock = artifacts.require('CrossChainEnabledOptimismMock'); +const CrossChainEnabledPolygonChildMock = artifacts.require('CrossChainEnabledPolygonChildMock'); + +function shouldBehaveLikeReceiver (sender = randomAddress()) { + it('should reject same-chain calls', async function () { + await expectRevertCustomError( + this.receiver.crossChainRestricted(), + 'NotCrossChainCall()', + ); + + await expectRevertCustomError( + this.receiver.crossChainOwnerRestricted(), + 'NotCrossChainCall()', + ); + }); + + it('should restrict to cross-chain call from a invalid sender', async function () { + await expectRevertCustomError( + this.bridge.call(sender, this.receiver, 'crossChainOwnerRestricted()'), + `InvalidCrossChainSender("${sender}", "${await this.receiver.owner()}")`, + ); + }); + + it('should grant access to cross-chain call from the owner', async function () { + await this.bridge.call( + await this.receiver.owner(), + this.receiver, + 'crossChainOwnerRestricted()', + ); + }); +} + +contract('CrossChainEnabled', function () { + describe('AMB', function () { + beforeEach(async function () { + this.bridge = await BridgeHelper.deploy('AMB'); + this.receiver = await CrossChainEnabledAMBMock.new(this.bridge.address); + }); + + shouldBehaveLikeReceiver(); + }); + + describe('Arbitrum-L1', function () { + beforeEach(async function () { + this.bridge = await BridgeHelper.deploy('Arbitrum-L1'); + this.receiver = await CrossChainEnabledArbitrumL1Mock.new(this.bridge.address); + }); + + shouldBehaveLikeReceiver(); + }); + + describe('Arbitrum-L2', function () { + beforeEach(async function () { + this.bridge = await BridgeHelper.deploy('Arbitrum-L2'); + this.receiver = await CrossChainEnabledArbitrumL2Mock.new(); + }); + + shouldBehaveLikeReceiver(); + }); + + describe('Optimism', function () { + beforeEach(async function () { + this.bridge = await BridgeHelper.deploy('Optimism'); + this.receiver = await CrossChainEnabledOptimismMock.new(this.bridge.address); + }); + + shouldBehaveLikeReceiver(); + }); + + describe('Polygon-Child', function () { + beforeEach(async function () { + this.bridge = await BridgeHelper.deploy('Polygon-Child'); + this.receiver = await CrossChainEnabledPolygonChildMock.new(this.bridge.address); + }); + + shouldBehaveLikeReceiver(); + }); +}); diff --git a/lib/openzeppelin-contracts/test/finance/PaymentSplitter.test.js b/lib/openzeppelin-contracts/test/finance/PaymentSplitter.test.js new file mode 100644 index 0000000..2fa7a26 --- /dev/null +++ b/lib/openzeppelin-contracts/test/finance/PaymentSplitter.test.js @@ -0,0 +1,217 @@ +const { balance, constants, ether, expectEvent, send, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const PaymentSplitter = artifacts.require('PaymentSplitter'); +const Token = artifacts.require('ERC20Mock'); + +contract('PaymentSplitter', function (accounts) { + const [ owner, payee1, payee2, payee3, nonpayee1, payer1 ] = accounts; + + const amount = ether('1'); + + it('rejects an empty set of payees', async function () { + await expectRevert(PaymentSplitter.new([], []), 'PaymentSplitter: no payees'); + }); + + it('rejects more payees than shares', async function () { + await expectRevert(PaymentSplitter.new([payee1, payee2, payee3], [20, 30]), + 'PaymentSplitter: payees and shares length mismatch', + ); + }); + + it('rejects more shares than payees', async function () { + await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 30, 40]), + 'PaymentSplitter: payees and shares length mismatch', + ); + }); + + it('rejects null payees', async function () { + await expectRevert(PaymentSplitter.new([payee1, ZERO_ADDRESS], [20, 30]), + 'PaymentSplitter: account is the zero address', + ); + }); + + it('rejects zero-valued shares', async function () { + await expectRevert(PaymentSplitter.new([payee1, payee2], [20, 0]), + 'PaymentSplitter: shares are 0', + ); + }); + + it('rejects repeated payees', async function () { + await expectRevert(PaymentSplitter.new([payee1, payee1], [20, 30]), + 'PaymentSplitter: account already has shares', + ); + }); + + context('once deployed', function () { + beforeEach(async function () { + this.payees = [payee1, payee2, payee3]; + this.shares = [20, 10, 70]; + + this.contract = await PaymentSplitter.new(this.payees, this.shares); + this.token = await Token.new('MyToken', 'MT', owner, ether('1000')); + }); + + it('has total shares', async function () { + expect(await this.contract.totalShares()).to.be.bignumber.equal('100'); + }); + + it('has payees', async function () { + await Promise.all(this.payees.map(async (payee, index) => { + expect(await this.contract.payee(index)).to.equal(payee); + expect(await this.contract.released(payee)).to.be.bignumber.equal('0'); + expect(await this.contract.releasable(payee)).to.be.bignumber.equal('0'); + })); + }); + + describe('accepts payments', function () { + it('Ether', async function () { + await send.ether(owner, this.contract.address, amount); + + expect(await balance.current(this.contract.address)).to.be.bignumber.equal(amount); + }); + + it('Token', async function () { + await this.token.transfer(this.contract.address, amount, { from: owner }); + + expect(await this.token.balanceOf(this.contract.address)).to.be.bignumber.equal(amount); + }); + }); + + describe('shares', function () { + it('stores shares if address is payee', async function () { + expect(await this.contract.shares(payee1)).to.be.bignumber.not.equal('0'); + }); + + it('does not store shares if address is not payee', async function () { + expect(await this.contract.shares(nonpayee1)).to.be.bignumber.equal('0'); + }); + }); + + describe('release', function () { + describe('Ether', function () { + it('reverts if no funds to claim', async function () { + await expectRevert(this.contract.release(payee1), + 'PaymentSplitter: account is not due payment', + ); + }); + it('reverts if non-payee want to claim', async function () { + await send.ether(payer1, this.contract.address, amount); + await expectRevert(this.contract.release(nonpayee1), + 'PaymentSplitter: account has no shares', + ); + }); + }); + + describe('Token', function () { + it('reverts if no funds to claim', async function () { + await expectRevert(this.contract.release(this.token.address, payee1), + 'PaymentSplitter: account is not due payment', + ); + }); + it('reverts if non-payee want to claim', async function () { + await this.token.transfer(this.contract.address, amount, { from: owner }); + await expectRevert(this.contract.release(this.token.address, nonpayee1), + 'PaymentSplitter: account has no shares', + ); + }); + }); + }); + + describe('tracks releasable and released', function () { + it('Ether', async function () { + await send.ether(payer1, this.contract.address, amount); + const payment = amount.divn(10); + expect(await this.contract.releasable(payee2)).to.be.bignumber.equal(payment); + await this.contract.release(payee2); + expect(await this.contract.releasable(payee2)).to.be.bignumber.equal('0'); + expect(await this.contract.released(payee2)).to.be.bignumber.equal(payment); + }); + + it('Token', async function () { + await this.token.transfer(this.contract.address, amount, { from: owner }); + const payment = amount.divn(10); + expect(await this.contract.releasable(this.token.address, payee2, {})).to.be.bignumber.equal(payment); + await this.contract.release(this.token.address, payee2); + expect(await this.contract.releasable(this.token.address, payee2, {})).to.be.bignumber.equal('0'); + expect(await this.contract.released(this.token.address, payee2)).to.be.bignumber.equal(payment); + }); + }); + + describe('distributes funds to payees', function () { + it('Ether', async function () { + await send.ether(payer1, this.contract.address, amount); + + // receive funds + const initBalance = await balance.current(this.contract.address); + expect(initBalance).to.be.bignumber.equal(amount); + + // distribute to payees + + const tracker1 = await balance.tracker(payee1); + const receipt1 = await this.contract.release(payee1); + const profit1 = await tracker1.delta(); + expect(profit1).to.be.bignumber.equal(ether('0.20')); + expectEvent(receipt1, 'PaymentReleased', { to: payee1, amount: profit1 }); + + const tracker2 = await balance.tracker(payee2); + const receipt2 = await this.contract.release(payee2); + const profit2 = await tracker2.delta(); + expect(profit2).to.be.bignumber.equal(ether('0.10')); + expectEvent(receipt2, 'PaymentReleased', { to: payee2, amount: profit2 }); + + const tracker3 = await balance.tracker(payee3); + const receipt3 = await this.contract.release(payee3); + const profit3 = await tracker3.delta(); + expect(profit3).to.be.bignumber.equal(ether('0.70')); + expectEvent(receipt3, 'PaymentReleased', { to: payee3, amount: profit3 }); + + // end balance should be zero + expect(await balance.current(this.contract.address)).to.be.bignumber.equal('0'); + + // check correct funds released accounting + expect(await this.contract.totalReleased()).to.be.bignumber.equal(initBalance); + }); + + it('Token', async function () { + expect(await this.token.balanceOf(payee1)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOf(payee2)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOf(payee3)).to.be.bignumber.equal('0'); + + await this.token.transfer(this.contract.address, amount, { from: owner }); + + expectEvent( + await this.contract.release(this.token.address, payee1), + 'ERC20PaymentReleased', + { token: this.token.address, to: payee1, amount: ether('0.20') }, + ); + + await this.token.transfer(this.contract.address, amount, { from: owner }); + + expectEvent( + await this.contract.release(this.token.address, payee1), + 'ERC20PaymentReleased', + { token: this.token.address, to: payee1, amount: ether('0.20') }, + ); + + expectEvent( + await this.contract.release(this.token.address, payee2), + 'ERC20PaymentReleased', + { token: this.token.address, to: payee2, amount: ether('0.20') }, + ); + + expectEvent( + await this.contract.release(this.token.address, payee3), + 'ERC20PaymentReleased', + { token: this.token.address, to: payee3, amount: ether('1.40') }, + ); + + expect(await this.token.balanceOf(payee1)).to.be.bignumber.equal(ether('0.40')); + expect(await this.token.balanceOf(payee2)).to.be.bignumber.equal(ether('0.20')); + expect(await this.token.balanceOf(payee3)).to.be.bignumber.equal(ether('1.40')); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/finance/VestingWallet.behavior.js b/lib/openzeppelin-contracts/test/finance/VestingWallet.behavior.js new file mode 100644 index 0000000..d1d2fbf --- /dev/null +++ b/lib/openzeppelin-contracts/test/finance/VestingWallet.behavior.js @@ -0,0 +1,74 @@ +const { time } = require('@nomicfoundation/hardhat-network-helpers'); +const { expectEvent } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +function releasedEvent (token, amount) { + return token + ? [ 'ERC20Released', { token: token.address, amount } ] + : [ 'EtherReleased', { amount } ]; +} + +function shouldBehaveLikeVesting (beneficiary) { + it('check vesting schedule', async function () { + const [ fnVestedAmount, fnReleasable, ...args ] = this.token + ? [ 'vestedAmount(address,uint64)', 'releasable(address)', this.token.address ] + : [ 'vestedAmount(uint64)', 'releasable()' ]; + + for (const timestamp of this.schedule) { + await time.increaseTo(timestamp); + const vesting = this.vestingFn(timestamp); + + expect(await this.mock.methods[fnVestedAmount](...args, timestamp)) + .to.be.bignumber.equal(vesting); + + expect(await this.mock.methods[fnReleasable](...args)) + .to.be.bignumber.equal(vesting); + } + }); + + it('execute vesting schedule', async function () { + const [ fnRelease, ...args ] = this.token + ? [ 'release(address)', this.token.address ] + : [ 'release()' ]; + + let released = web3.utils.toBN(0); + const before = await this.getBalance(beneficiary); + + { + const receipt = await this.mock.methods[fnRelease](...args); + + await expectEvent.inTransaction( + receipt.tx, + this.mock, + ...releasedEvent(this.token, '0'), + ); + + await this.checkRelease(receipt, beneficiary, '0'); + + expect(await this.getBalance(beneficiary)).to.be.bignumber.equal(before); + } + + for (const timestamp of this.schedule) { + await time.setNextBlockTimestamp(timestamp); + const vested = this.vestingFn(timestamp); + + const receipt = await this.mock.methods[fnRelease](...args); + await expectEvent.inTransaction( + receipt.tx, + this.mock, + ...releasedEvent(this.token, vested.sub(released)), + ); + + await this.checkRelease(receipt, beneficiary, vested.sub(released)); + + expect(await this.getBalance(beneficiary)) + .to.be.bignumber.equal(before.add(vested)); + + released = vested; + } + }); +} + +module.exports = { + shouldBehaveLikeVesting, +}; diff --git a/lib/openzeppelin-contracts/test/finance/VestingWallet.test.js b/lib/openzeppelin-contracts/test/finance/VestingWallet.test.js new file mode 100644 index 0000000..6aa7378 --- /dev/null +++ b/lib/openzeppelin-contracts/test/finance/VestingWallet.test.js @@ -0,0 +1,67 @@ +const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { web3 } = require('@openzeppelin/test-helpers/src/setup'); +const { expect } = require('chai'); + +const ERC20Mock = artifacts.require('ERC20Mock'); +const VestingWallet = artifacts.require('VestingWallet'); + +const { shouldBehaveLikeVesting } = require('./VestingWallet.behavior'); + +const min = (...args) => args.slice(1).reduce((x, y) => x.lt(y) ? x : y, args[0]); + +contract('VestingWallet', function (accounts) { + const [ sender, beneficiary ] = accounts; + + const amount = web3.utils.toBN(web3.utils.toWei('100')); + const duration = web3.utils.toBN(4 * 365 * 86400); // 4 years + + beforeEach(async function () { + this.start = (await time.latest()).addn(3600); // in 1 hour + this.mock = await VestingWallet.new(beneficiary, this.start, duration); + }); + + it('rejects zero address for beneficiary', async function () { + await expectRevert( + VestingWallet.new(constants.ZERO_ADDRESS, this.start, duration), + 'VestingWallet: beneficiary is zero address', + ); + }); + + it('check vesting contract', async function () { + expect(await this.mock.beneficiary()).to.be.equal(beneficiary); + expect(await this.mock.start()).to.be.bignumber.equal(this.start); + expect(await this.mock.duration()).to.be.bignumber.equal(duration); + }); + + describe('vesting schedule', function () { + beforeEach(async function () { + this.schedule = Array(64).fill().map((_, i) => web3.utils.toBN(i).mul(duration).divn(60).add(this.start)); + this.vestingFn = timestamp => min(amount, amount.mul(timestamp.sub(this.start)).div(duration)); + }); + + describe('Eth vesting', function () { + beforeEach(async function () { + await web3.eth.sendTransaction({ from: sender, to: this.mock.address, value: amount }); + this.getBalance = account => web3.eth.getBalance(account).then(web3.utils.toBN); + this.checkRelease = () => {}; + }); + + shouldBehaveLikeVesting(beneficiary); + }); + + describe('ERC20 vesting', function () { + beforeEach(async function () { + this.token = await ERC20Mock.new('Name', 'Symbol', this.mock.address, amount); + this.getBalance = (account) => this.token.balanceOf(account); + this.checkRelease = (receipt, to, value) => expectEvent.inTransaction( + receipt.tx, + this.token, + 'Transfer', + { from: this.mock.address, to, value }, + ); + }); + + shouldBehaveLikeVesting(beneficiary); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/Governor.test.js b/lib/openzeppelin-contracts/test/governance/Governor.test.js new file mode 100644 index 0000000..bd10dcd --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/Governor.test.js @@ -0,0 +1,632 @@ +const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; +const { fromRpcSig } = require('ethereumjs-util'); +const Enums = require('../helpers/enums'); +const { EIP712Domain } = require('../helpers/eip712'); +const { GovernorHelper } = require('../helpers/governance'); + +const { + shouldSupportInterfaces, +} = require('../utils/introspection/SupportsInterface.behavior'); + +const Token = artifacts.require('ERC20VotesMock'); +const Governor = artifacts.require('GovernorMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); +const ERC721Mock = artifacts.require('ERC721Mock'); +const ERC1155Mock = artifacts.require('ERC1155Mock'); + +contract('Governor', function (accounts) { + const [ owner, proposer, voter1, voter2, voter3, voter4 ] = accounts; + const empty = web3.utils.toChecksumAddress(web3.utils.randomHex(20)); + + const name = 'OZ-Governor'; + const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + this.chainId = await web3.eth.getChainId(); + this.token = await Token.new(tokenName, tokenSymbol); + this.mock = await Governor.new(name, this.token.address, votingDelay, votingPeriod, 10); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + value, + }, + ], ''); + }); + + shouldSupportInterfaces([ + 'ERC165', + 'ERC1155Receiver', + 'Governor', + 'GovernorWithParams', + ]); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + expect(await this.mock.COUNTING_MODE()).to.be.equal('support=bravo&quorum=for,abstain'); + }); + + it('nominal workflow', async function () { + // Before + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(false); + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(value); + expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0'); + + // Run proposal + const txPropose = await this.helper.propose({ from: proposer }); + + expectEvent( + txPropose, + 'ProposalCreated', + { + proposalId: this.proposal.id, + proposer, + targets: this.proposal.targets, + // values: this.proposal.values, + signatures: this.proposal.signatures, + calldatas: this.proposal.data, + startBlock: new BN(txPropose.receipt.blockNumber).add(votingDelay), + endBlock: new BN(txPropose.receipt.blockNumber).add(votingDelay).add(votingPeriod), + description: this.proposal.description, + }, + ); + + await this.helper.waitForSnapshot(); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.For, reason: 'This is nice' }, { from: voter1 }), + 'VoteCast', + { + voter: voter1, + support: Enums.VoteType.For, + reason: 'This is nice', + weight: web3.utils.toWei('10'), + }, + ); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }), + 'VoteCast', + { + voter: voter2, + support: Enums.VoteType.For, + weight: web3.utils.toWei('7'), + }, + ); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }), + 'VoteCast', + { + voter: voter3, + support: Enums.VoteType.Against, + weight: web3.utils.toWei('5'), + }, + ); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }), + 'VoteCast', + { + voter: voter4, + support: Enums.VoteType.Abstain, + weight: web3.utils.toWei('2'), + }, + ); + + await this.helper.waitForDeadline(); + + const txExecute = await this.helper.execute(); + + expectEvent( + txExecute, + 'ProposalExecuted', + { proposalId: this.proposal.id }, + ); + + await expectEvent.inTransaction( + txExecute.tx, + this.receiver, + 'MockFunctionCalled', + ); + + // After + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true); + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0'); + expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(value); + }); + + it('vote with signature', async function () { + const voterBySig = Wallet.generate(); + const voterBySigAddress = web3.utils.toChecksumAddress(voterBySig.getAddressString()); + + const signature = async (message) => { + return fromRpcSig(ethSigUtil.signTypedMessage( + voterBySig.getPrivateKey(), + { + data: { + types: { + EIP712Domain, + Ballot: [ + { name: 'proposalId', type: 'uint256' }, + { name: 'support', type: 'uint8' }, + ], + }, + domain: { name, version, chainId: this.chainId, verifyingContract: this.mock.address }, + primaryType: 'Ballot', + message, + }, + }, + )); + }; + + await this.token.delegate(voterBySigAddress, { from: voter1 }); + + // Run proposal + await this.helper.propose(); + await this.helper.waitForSnapshot(); + expectEvent( + await this.helper.vote({ support: Enums.VoteType.For, signature }), + 'VoteCast', + { voter: voterBySigAddress, support: Enums.VoteType.For }, + ); + await this.helper.waitForDeadline(); + await this.helper.execute(); + + // After + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voterBySigAddress)).to.be.equal(true); + }); + + it('send ethers', async function () { + this.proposal = this.helper.setProposal([ + { + target: empty, + value, + }, + ], ''); + + // Before + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(value); + expect(await web3.eth.getBalance(empty)).to.be.bignumber.equal('0'); + + // Run proposal + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + + // After + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0'); + expect(await web3.eth.getBalance(empty)).to.be.bignumber.equal(value); + }); + + describe('should revert', function () { + describe('on propose', function () { + it('if proposal already exists', async function () { + await this.helper.propose(); + await expectRevert(this.helper.propose(), 'Governor: proposal already exists'); + }); + }); + + describe('on vote', function () { + it('if proposal does not exist', async function () { + await expectRevert( + this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'Governor: unknown proposal id', + ); + }); + + it('if voting has not started', async function () { + await this.helper.propose(); + await expectRevert( + this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'Governor: vote not currently active', + ); + }); + + it('if support value is invalid', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await expectRevert( + this.helper.vote({ support: new BN('255') }), + 'GovernorVotingSimple: invalid value for enum VoteType', + ); + }); + + it('if vote was already casted', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await expectRevert( + this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'GovernorVotingSimple: vote already cast', + ); + }); + + it('if voting is over', async function () { + await this.helper.propose(); + await this.helper.waitForDeadline(); + await expectRevert( + this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'Governor: vote not currently active', + ); + }); + }); + + describe('on execute', function () { + it('if proposal does not exist', async function () { + await expectRevert(this.helper.execute(), 'Governor: unknown proposal id'); + }); + + it('if quorum is not reached', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter3 }); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('if score not reached', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter1 }); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('if voting is not over', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('if receiver revert without reason', async function () { + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + data: this.receiver.contract.methods.mockFunctionRevertsNoReason().encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await expectRevert(this.helper.execute(), 'Governor: call reverted without message'); + }); + + it('if receiver revert with reason', async function () { + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + data: this.receiver.contract.methods.mockFunctionRevertsReason().encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await expectRevert(this.helper.execute(), 'CallReceiverMock: reverting'); + }); + + it('if proposal was already executed', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + }); + }); + + describe('state', function () { + it('Unset', async function () { + await expectRevert(this.mock.state(this.proposal.id), 'Governor: unknown proposal id'); + }); + + it('Pending & Active', async function () { + await this.helper.propose(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Pending); + await this.helper.waitForSnapshot(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Pending); + await this.helper.waitForSnapshot(+1); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active); + }); + + it('Defeated', async function () { + await this.helper.propose(); + await this.helper.waitForDeadline(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active); + await this.helper.waitForDeadline(+1); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Defeated); + }); + + it('Succeeded', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active); + await this.helper.waitForDeadline(+1); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded); + }); + + it('Executed', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Executed); + }); + }); + + describe('cancel', function () { + it('before proposal', async function () { + await expectRevert(this.helper.cancel(), 'Governor: unknown proposal id'); + }); + + it('after proposal', async function () { + await this.helper.propose(); + + await this.helper.cancel(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + + await this.helper.waitForSnapshot(); + await expectRevert( + this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'Governor: vote not currently active', + ); + }); + + it('after vote', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + + await this.helper.cancel(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + + await this.helper.waitForDeadline(); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('after deadline', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + await this.helper.cancel(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('after execution', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + + await expectRevert(this.helper.cancel(), 'Governor: proposal not active'); + }); + }); + + describe('proposal length', function () { + it('empty', async function () { + this.helper.setProposal([ ], ''); + await expectRevert(this.helper.propose(), 'Governor: empty proposal'); + }); + + it('missmatch #1', async function () { + this.helper.setProposal({ + targets: [ ], + values: [ web3.utils.toWei('0') ], + data: [ this.receiver.contract.methods.mockFunction().encodeABI() ], + }, ''); + await expectRevert(this.helper.propose(), 'Governor: invalid proposal length'); + }); + + it('missmatch #2', async function () { + this.helper.setProposal({ + targets: [ this.receiver.address ], + values: [ ], + data: [ this.receiver.contract.methods.mockFunction().encodeABI() ], + }, ''); + await expectRevert(this.helper.propose(), 'Governor: invalid proposal length'); + }); + + it('missmatch #3', async function () { + this.helper.setProposal({ + targets: [ this.receiver.address ], + values: [ web3.utils.toWei('0') ], + data: [ ], + }, ''); + await expectRevert(this.helper.propose(), 'Governor: invalid proposal length'); + }); + }); + + describe('onlyGovernance updates', function () { + it('setVotingDelay is protected', async function () { + await expectRevert(this.mock.setVotingDelay('0'), 'Governor: onlyGovernance'); + }); + + it('setVotingPeriod is protected', async function () { + await expectRevert(this.mock.setVotingPeriod('32'), 'Governor: onlyGovernance'); + }); + + it('setProposalThreshold is protected', async function () { + await expectRevert(this.mock.setProposalThreshold('1000000000000000000'), 'Governor: onlyGovernance'); + }); + + it('can setVotingDelay through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.setVotingDelay('0').encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.execute(), + 'VotingDelaySet', + { oldVotingDelay: '4', newVotingDelay: '0' }, + ); + + expect(await this.mock.votingDelay()).to.be.bignumber.equal('0'); + }); + + it('can setVotingPeriod through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.setVotingPeriod('32').encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.execute(), + 'VotingPeriodSet', + { oldVotingPeriod: '16', newVotingPeriod: '32' }, + ); + + expect(await this.mock.votingPeriod()).to.be.bignumber.equal('32'); + }); + + it('cannot setVotingPeriod to 0 through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.setVotingPeriod('0').encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + await expectRevert(this.helper.execute(), 'GovernorSettings: voting period too low'); + }); + + it('can setProposalThreshold to 0 through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.setProposalThreshold('1000000000000000000').encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.execute(), + 'ProposalThresholdSet', + { oldProposalThreshold: '0', newProposalThreshold: '1000000000000000000' }, + ); + + expect(await this.mock.proposalThreshold()).to.be.bignumber.equal('1000000000000000000'); + }); + }); + + describe('safe receive', function () { + describe('ERC721', function () { + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + const tokenId = new BN(1); + + beforeEach(async function () { + this.token = await ERC721Mock.new(name, symbol); + await this.token.mint(owner, tokenId); + }); + + it('can receive an ERC721 safeTransfer', async function () { + await this.token.safeTransferFrom(owner, this.mock.address, tokenId, { from: owner }); + }); + }); + + describe('ERC1155', function () { + const uri = 'https://token-cdn-domain/{id}.json'; + const tokenIds = { + 1: new BN(1000), + 2: new BN(2000), + 3: new BN(3000), + }; + + beforeEach(async function () { + this.token = await ERC1155Mock.new(uri); + await this.token.mintBatch(owner, Object.keys(tokenIds), Object.values(tokenIds), '0x'); + }); + + it('can receive ERC1155 safeTransfer', async function () { + await this.token.safeTransferFrom( + owner, + this.mock.address, + ...Object.entries(tokenIds)[0], // id + amount + '0x', + { from: owner }, + ); + }); + + it('can receive ERC1155 safeBatchTransfer', async function () { + await this.token.safeBatchTransferFrom( + owner, + this.mock.address, + Object.keys(tokenIds), + Object.values(tokenIds), + '0x', + { from: owner }, + ); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/TimelockController.test.js b/lib/openzeppelin-contracts/test/governance/TimelockController.test.js new file mode 100644 index 0000000..e0a3244 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/TimelockController.test.js @@ -0,0 +1,1134 @@ +const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS, ZERO_BYTES32 } = constants; + +const { expect } = require('chai'); + +const { + shouldSupportInterfaces, +} = require('../utils/introspection/SupportsInterface.behavior'); + +const TimelockController = artifacts.require('TimelockController'); +const CallReceiverMock = artifacts.require('CallReceiverMock'); +const Implementation2 = artifacts.require('Implementation2'); +const ERC721Mock = artifacts.require('ERC721Mock'); +const ERC1155Mock = artifacts.require('ERC1155Mock'); + +const MINDELAY = time.duration.days(1); + +const salt = '0x025e7b0be353a74631ad648c667493c0e1cd31caa4cc2d3520fdc171ea0cc726'; // a random value + +function genOperation (target, value, data, predecessor, salt) { + const id = web3.utils.keccak256(web3.eth.abi.encodeParameters([ + 'address', + 'uint256', + 'bytes', + 'uint256', + 'bytes32', + ], [ + target, + value, + data, + predecessor, + salt, + ])); + return { id, target, value, data, predecessor, salt }; +} + +function genOperationBatch (targets, values, payloads, predecessor, salt) { + const id = web3.utils.keccak256(web3.eth.abi.encodeParameters([ + 'address[]', + 'uint256[]', + 'bytes[]', + 'uint256', + 'bytes32', + ], [ + targets, + values, + payloads, + predecessor, + salt, + ])); + return { id, targets, values, payloads, predecessor, salt }; +} + +contract('TimelockController', function (accounts) { + const [ , admin, proposer, canceller, executor, other ] = accounts; + + const TIMELOCK_ADMIN_ROLE = web3.utils.soliditySha3('TIMELOCK_ADMIN_ROLE'); + const PROPOSER_ROLE = web3.utils.soliditySha3('PROPOSER_ROLE'); + const EXECUTOR_ROLE = web3.utils.soliditySha3('EXECUTOR_ROLE'); + const CANCELLER_ROLE = web3.utils.soliditySha3('CANCELLER_ROLE'); + + beforeEach(async function () { + // Deploy new timelock + this.mock = await TimelockController.new( + MINDELAY, + [ proposer ], + [ executor ], + admin, + ); + + expect(await this.mock.hasRole(CANCELLER_ROLE, proposer)).to.be.equal(true); + await this.mock.revokeRole(CANCELLER_ROLE, proposer, { from: admin }); + await this.mock.grantRole(CANCELLER_ROLE, canceller, { from: admin }); + + // Mocks + this.callreceivermock = await CallReceiverMock.new({ from: admin }); + this.implementation2 = await Implementation2.new({ from: admin }); + }); + + shouldSupportInterfaces([ + 'ERC1155Receiver', + ]); + + it('initial state', async function () { + expect(await this.mock.getMinDelay()).to.be.bignumber.equal(MINDELAY); + + expect(await this.mock.TIMELOCK_ADMIN_ROLE()).to.be.equal(TIMELOCK_ADMIN_ROLE); + expect(await this.mock.PROPOSER_ROLE()).to.be.equal(PROPOSER_ROLE); + expect(await this.mock.EXECUTOR_ROLE()).to.be.equal(EXECUTOR_ROLE); + expect(await this.mock.CANCELLER_ROLE()).to.be.equal(CANCELLER_ROLE); + + expect(await Promise.all([ PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE ].map(role => + this.mock.hasRole(role, proposer), + ))).to.be.deep.equal([ true, false, false ]); + + expect(await Promise.all([ PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE ].map(role => + this.mock.hasRole(role, canceller), + ))).to.be.deep.equal([ false, true, false ]); + + expect(await Promise.all([ PROPOSER_ROLE, CANCELLER_ROLE, EXECUTOR_ROLE ].map(role => + this.mock.hasRole(role, executor), + ))).to.be.deep.equal([ false, false, true ]); + }); + + it('optional admin', async function () { + const mock = await TimelockController.new( + MINDELAY, + [ proposer ], + [ executor ], + ZERO_ADDRESS, + { from: other }, + ); + + expect(await mock.hasRole(TIMELOCK_ADMIN_ROLE, admin)).to.be.equal(false); + expect(await mock.hasRole(TIMELOCK_ADMIN_ROLE, other)).to.be.equal(false); + }); + + describe('methods', function () { + describe('operation hashing', function () { + it('hashOperation', async function () { + this.operation = genOperation( + '0x29cebefe301c6ce1bb36b58654fea275e1cacc83', + '0xf94fdd6e21da21d2', + '0xa3bc5104', + '0xba41db3be0a9929145cfe480bd0f1f003689104d275ae912099f925df424ef94', + '0x60d9109846ab510ed75c15f979ae366a8a2ace11d34ba9788c13ac296db50e6e', + ); + expect(await this.mock.hashOperation( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + )).to.be.equal(this.operation.id); + }); + + it('hashOperationBatch', async function () { + this.operation = genOperationBatch( + Array(8).fill('0x2d5f21620e56531c1d59c2df9b8e95d129571f71'), + Array(8).fill('0x2b993cfce932ccee'), + Array(8).fill('0xcf51966b'), + '0xce8f45069cc71d25f71ba05062de1a3974f9849b004de64a70998bca9d29c2e7', + '0x8952d74c110f72bfe5accdf828c74d53a7dfb71235dfa8a1e8c75d8576b372ff', + ); + expect(await this.mock.hashOperationBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + )).to.be.equal(this.operation.id); + }); + }); + describe('simple', function () { + describe('schedule', function () { + beforeEach(async function () { + this.operation = genOperation( + '0x31754f590B97fD975Eb86938f18Cc304E264D2F2', + 0, + '0x3bf92ccc', + ZERO_BYTES32, + salt, + ); + }); + + it('proposer can schedule', async function () { + const receipt = await this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ); + expectEvent(receipt, 'CallScheduled', { + id: this.operation.id, + index: web3.utils.toBN(0), + target: this.operation.target, + value: web3.utils.toBN(this.operation.value), + data: this.operation.data, + predecessor: this.operation.predecessor, + delay: MINDELAY, + }); + + const block = await web3.eth.getBlock(receipt.receipt.blockHash); + + expect(await this.mock.getTimestamp(this.operation.id)) + .to.be.bignumber.equal(web3.utils.toBN(block.timestamp).add(MINDELAY)); + }); + + it('prevent overwriting active operation', async function () { + await this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ); + + await expectRevert( + this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ), + 'TimelockController: operation already scheduled', + ); + }); + + it('prevent non-proposer from committing', async function () { + await expectRevert( + this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: other }, + ), + `AccessControl: account ${other.toLowerCase()} is missing role ${PROPOSER_ROLE}`, + ); + }); + + it('enforce minimum delay', async function () { + await expectRevert( + this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY - 1, + { from: proposer }, + ), + 'TimelockController: insufficient delay', + ); + }); + }); + + describe('execute', function () { + beforeEach(async function () { + this.operation = genOperation( + '0xAe22104DCD970750610E6FE15E623468A98b15f7', + 0, + '0x13e414de', + ZERO_BYTES32, + '0xc1059ed2dc130227aa1d1d539ac94c641306905c020436c636e19e3fab56fc7f', + ); + }); + + it('revert if operation is not scheduled', async function () { + await expectRevert( + this.mock.execute( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: operation is not ready', + ); + }); + + describe('with scheduled operation', function () { + beforeEach(async function () { + ({ receipt: this.receipt, logs: this.logs } = await this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + )); + }); + + it('revert if execution comes too early 1/2', async function () { + await expectRevert( + this.mock.execute( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: operation is not ready', + ); + }); + + it('revert if execution comes too early 2/2', async function () { + const timestamp = await this.mock.getTimestamp(this.operation.id); + await time.increaseTo(timestamp - 5); // -1 is too tight, test sometime fails + + await expectRevert( + this.mock.execute( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: operation is not ready', + ); + }); + + describe('on time', function () { + beforeEach(async function () { + const timestamp = await this.mock.getTimestamp(this.operation.id); + await time.increaseTo(timestamp); + }); + + it('executor can reveal', async function () { + const receipt = await this.mock.execute( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ); + expectEvent(receipt, 'CallExecuted', { + id: this.operation.id, + index: web3.utils.toBN(0), + target: this.operation.target, + value: web3.utils.toBN(this.operation.value), + data: this.operation.data, + }); + }); + + it('prevent non-executor from revealing', async function () { + await expectRevert( + this.mock.execute( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + { from: other }, + ), + `AccessControl: account ${other.toLowerCase()} is missing role ${EXECUTOR_ROLE}`, + ); + }); + }); + }); + }); + }); + + describe('batch', function () { + describe('schedule', function () { + beforeEach(async function () { + this.operation = genOperationBatch( + Array(8).fill('0xEd912250835c812D4516BBD80BdaEA1bB63a293C'), + Array(8).fill(0), + Array(8).fill('0x2fcb7a88'), + ZERO_BYTES32, + '0x6cf9d042ade5de78bed9ffd075eb4b2a4f6b1736932c2dc8af517d6e066f51f5', + ); + }); + + it('proposer can schedule', async function () { + const receipt = await this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ); + for (const i in this.operation.targets) { + expectEvent(receipt, 'CallScheduled', { + id: this.operation.id, + index: web3.utils.toBN(i), + target: this.operation.targets[i], + value: web3.utils.toBN(this.operation.values[i]), + data: this.operation.payloads[i], + predecessor: this.operation.predecessor, + delay: MINDELAY, + }); + } + + const block = await web3.eth.getBlock(receipt.receipt.blockHash); + + expect(await this.mock.getTimestamp(this.operation.id)) + .to.be.bignumber.equal(web3.utils.toBN(block.timestamp).add(MINDELAY)); + }); + + it('prevent overwriting active operation', async function () { + await this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ); + + await expectRevert( + this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ), + 'TimelockController: operation already scheduled', + ); + }); + + it('length of batch parameter must match #1', async function () { + await expectRevert( + this.mock.scheduleBatch( + this.operation.targets, + [], + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ), + 'TimelockController: length mismatch', + ); + }); + + it('length of batch parameter must match #1', async function () { + await expectRevert( + this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + [], + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + ), + 'TimelockController: length mismatch', + ); + }); + + it('prevent non-proposer from committing', async function () { + await expectRevert( + this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: other }, + ), + `AccessControl: account ${other.toLowerCase()} is missing role ${PROPOSER_ROLE}`, + ); + }); + + it('enforce minimum delay', async function () { + await expectRevert( + this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY - 1, + { from: proposer }, + ), + 'TimelockController: insufficient delay', + ); + }); + }); + + describe('execute', function () { + beforeEach(async function () { + this.operation = genOperationBatch( + Array(8).fill('0x76E53CcEb05131Ef5248553bEBDb8F70536830b1'), + Array(8).fill(0), + Array(8).fill('0x58a60f63'), + ZERO_BYTES32, + '0x9545eeabc7a7586689191f78a5532443698538e54211b5bd4d7dc0fc0102b5c7', + ); + }); + + it('revert if operation is not scheduled', async function () { + await expectRevert( + this.mock.executeBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: operation is not ready', + ); + }); + + describe('with scheduled operation', function () { + beforeEach(async function () { + ({ receipt: this.receipt, logs: this.logs } = await this.mock.scheduleBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + )); + }); + + it('revert if execution comes too early 1/2', async function () { + await expectRevert( + this.mock.executeBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: operation is not ready', + ); + }); + + it('revert if execution comes too early 2/2', async function () { + const timestamp = await this.mock.getTimestamp(this.operation.id); + await time.increaseTo(timestamp - 5); // -1 is to tight, test sometime fails + + await expectRevert( + this.mock.executeBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: operation is not ready', + ); + }); + + describe('on time', function () { + beforeEach(async function () { + const timestamp = await this.mock.getTimestamp(this.operation.id); + await time.increaseTo(timestamp); + }); + + it('executor can reveal', async function () { + const receipt = await this.mock.executeBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ); + for (const i in this.operation.targets) { + expectEvent(receipt, 'CallExecuted', { + id: this.operation.id, + index: web3.utils.toBN(i), + target: this.operation.targets[i], + value: web3.utils.toBN(this.operation.values[i]), + data: this.operation.payloads[i], + }); + } + }); + + it('prevent non-executor from revealing', async function () { + await expectRevert( + this.mock.executeBatch( + this.operation.targets, + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: other }, + ), + `AccessControl: account ${other.toLowerCase()} is missing role ${EXECUTOR_ROLE}`, + ); + }); + + it('length mismatch #1', async function () { + await expectRevert( + this.mock.executeBatch( + [], + this.operation.values, + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: length mismatch', + ); + }); + + it('length mismatch #2', async function () { + await expectRevert( + this.mock.executeBatch( + this.operation.targets, + [], + this.operation.payloads, + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: length mismatch', + ); + }); + + it('length mismatch #3', async function () { + await expectRevert( + this.mock.executeBatch( + this.operation.targets, + this.operation.values, + [], + this.operation.predecessor, + this.operation.salt, + { from: executor }, + ), + 'TimelockController: length mismatch', + ); + }); + }); + }); + + it('partial execution', async function () { + const operation = genOperationBatch( + [ + this.callreceivermock.address, + this.callreceivermock.address, + this.callreceivermock.address, + ], + [ + 0, + 0, + 0, + ], + [ + this.callreceivermock.contract.methods.mockFunction().encodeABI(), + this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(), + this.callreceivermock.contract.methods.mockFunction().encodeABI(), + ], + ZERO_BYTES32, + '0x8ac04aa0d6d66b8812fb41d39638d37af0a9ab11da507afd65c509f8ed079d3e', + ); + + await this.mock.scheduleBatch( + operation.targets, + operation.values, + operation.payloads, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + await expectRevert( + this.mock.executeBatch( + operation.targets, + operation.values, + operation.payloads, + operation.predecessor, + operation.salt, + { from: executor }, + ), + 'TimelockController: underlying transaction reverted', + ); + }); + }); + }); + + describe('cancel', function () { + beforeEach(async function () { + this.operation = genOperation( + '0xC6837c44AA376dbe1d2709F13879E040CAb653ca', + 0, + '0x296e58dd', + ZERO_BYTES32, + '0xa2485763600634800df9fc9646fb2c112cf98649c55f63dd1d9c7d13a64399d9', + ); + ({ receipt: this.receipt, logs: this.logs } = await this.mock.schedule( + this.operation.target, + this.operation.value, + this.operation.data, + this.operation.predecessor, + this.operation.salt, + MINDELAY, + { from: proposer }, + )); + }); + + it('canceller can cancel', async function () { + const receipt = await this.mock.cancel(this.operation.id, { from: canceller }); + expectEvent(receipt, 'Cancelled', { id: this.operation.id }); + }); + + it('cannot cancel invalid operation', async function () { + await expectRevert( + this.mock.cancel(constants.ZERO_BYTES32, { from: canceller }), + 'TimelockController: operation cannot be cancelled', + ); + }); + + it('prevent non-canceller from canceling', async function () { + await expectRevert( + this.mock.cancel(this.operation.id, { from: other }), + `AccessControl: account ${other.toLowerCase()} is missing role ${CANCELLER_ROLE}`, + ); + }); + }); + }); + + describe('maintenance', function () { + it('prevent unauthorized maintenance', async function () { + await expectRevert( + this.mock.updateDelay(0, { from: other }), + 'TimelockController: caller must be timelock', + ); + }); + + it('timelock scheduled maintenance', async function () { + const newDelay = time.duration.hours(6); + const operation = genOperation( + this.mock.address, + 0, + this.mock.contract.methods.updateDelay(newDelay.toString()).encodeABI(), + ZERO_BYTES32, + '0xf8e775b2c5f4d66fb5c7fa800f35ef518c262b6014b3c0aee6ea21bff157f108', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + const receipt = await this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor }, + ); + expectEvent(receipt, 'MinDelayChange', { newDuration: newDelay.toString(), oldDuration: MINDELAY }); + + expect(await this.mock.getMinDelay()).to.be.bignumber.equal(newDelay); + }); + }); + + describe('dependency', function () { + beforeEach(async function () { + this.operation1 = genOperation( + '0xdE66bD4c97304200A95aE0AadA32d6d01A867E39', + 0, + '0x01dc731a', + ZERO_BYTES32, + '0x64e932133c7677402ead2926f86205e2ca4686aebecf5a8077627092b9bb2feb', + ); + this.operation2 = genOperation( + '0x3c7944a3F1ee7fc8c5A5134ba7c79D11c3A1FCa3', + 0, + '0x8f531849', + this.operation1.id, + '0x036e1311cac523f9548e6461e29fb1f8f9196b91910a41711ea22f5de48df07d', + ); + await this.mock.schedule( + this.operation1.target, + this.operation1.value, + this.operation1.data, + this.operation1.predecessor, + this.operation1.salt, + MINDELAY, + { from: proposer }, + ); + await this.mock.schedule( + this.operation2.target, + this.operation2.value, + this.operation2.data, + this.operation2.predecessor, + this.operation2.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + }); + + it('cannot execute before dependency', async function () { + await expectRevert( + this.mock.execute( + this.operation2.target, + this.operation2.value, + this.operation2.data, + this.operation2.predecessor, + this.operation2.salt, + { from: executor }, + ), + 'TimelockController: missing dependency', + ); + }); + + it('can execute after dependency', async function () { + await this.mock.execute( + this.operation1.target, + this.operation1.value, + this.operation1.data, + this.operation1.predecessor, + this.operation1.salt, + { from: executor }, + ); + await this.mock.execute( + this.operation2.target, + this.operation2.value, + this.operation2.data, + this.operation2.predecessor, + this.operation2.salt, + { from: executor }, + ); + }); + }); + + describe('usage scenario', function () { + this.timeout(10000); + + it('call', async function () { + const operation = genOperation( + this.implementation2.address, + 0, + this.implementation2.contract.methods.setValue(42).encodeABI(), + ZERO_BYTES32, + '0x8043596363daefc89977b25f9d9b4d06c3910959ef0c4d213557a903e1b555e2', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + await this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor }, + ); + + expect(await this.implementation2.getValue()).to.be.bignumber.equal(web3.utils.toBN(42)); + }); + + it('call reverting', async function () { + const operation = genOperation( + this.callreceivermock.address, + 0, + this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(), + ZERO_BYTES32, + '0xb1b1b276fdf1a28d1e00537ea73b04d56639128b08063c1a2f70a52e38cba693', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + await expectRevert( + this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor }, + ), + 'TimelockController: underlying transaction reverted', + ); + }); + + it('call throw', async function () { + const operation = genOperation( + this.callreceivermock.address, + 0, + this.callreceivermock.contract.methods.mockFunctionThrows().encodeABI(), + ZERO_BYTES32, + '0xe5ca79f295fc8327ee8a765fe19afb58f4a0cbc5053642bfdd7e73bc68e0fc67', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + await expectRevert( + this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor }, + ), + 'TimelockController: underlying transaction reverted', + ); + }); + + it('call out of gas', async function () { + const operation = genOperation( + this.callreceivermock.address, + 0, + this.callreceivermock.contract.methods.mockFunctionOutOfGas().encodeABI(), + ZERO_BYTES32, + '0xf3274ce7c394c5b629d5215723563a744b817e1730cca5587c567099a14578fd', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + await expectRevert( + this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor, gas: '70000' }, + ), + 'TimelockController: underlying transaction reverted', + ); + }); + + it('call payable with eth', async function () { + const operation = genOperation( + this.callreceivermock.address, + 1, + this.callreceivermock.contract.methods.mockFunction().encodeABI(), + ZERO_BYTES32, + '0x5ab73cd33477dcd36c1e05e28362719d0ed59a7b9ff14939de63a43073dc1f44', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + + await this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor, value: 1 }, + ); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(1)); + }); + + it('call nonpayable with eth', async function () { + const operation = genOperation( + this.callreceivermock.address, + 1, + this.callreceivermock.contract.methods.mockFunctionNonPayable().encodeABI(), + ZERO_BYTES32, + '0xb78edbd920c7867f187e5aa6294ae5a656cfbf0dea1ccdca3751b740d0f2bdf8', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + + await expectRevert( + this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor }, + ), + 'TimelockController: underlying transaction reverted', + ); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + }); + + it('call reverting with eth', async function () { + const operation = genOperation( + this.callreceivermock.address, + 1, + this.callreceivermock.contract.methods.mockFunctionRevertsNoReason().encodeABI(), + ZERO_BYTES32, + '0xdedb4563ef0095db01d81d3f2decf57cf83e4a72aa792af14c43a792b56f4de6', + ); + + await this.mock.schedule( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + MINDELAY, + { from: proposer }, + ); + await time.increase(MINDELAY); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + + await expectRevert( + this.mock.execute( + operation.target, + operation.value, + operation.data, + operation.predecessor, + operation.salt, + { from: executor }, + ), + 'TimelockController: underlying transaction reverted', + ); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + expect(await web3.eth.getBalance(this.callreceivermock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + }); + }); + + describe('safe receive', function () { + describe('ERC721', function () { + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + const tokenId = new BN(1); + + beforeEach(async function () { + this.token = await ERC721Mock.new(name, symbol); + await this.token.mint(other, tokenId); + }); + + it('can receive an ERC721 safeTransfer', async function () { + await this.token.safeTransferFrom(other, this.mock.address, tokenId, { from: other }); + }); + }); + + describe('ERC1155', function () { + const uri = 'https://token-cdn-domain/{id}.json'; + const tokenIds = { + 1: new BN(1000), + 2: new BN(2000), + 3: new BN(3000), + }; + + beforeEach(async function () { + this.token = await ERC1155Mock.new(uri); + await this.token.mintBatch(other, Object.keys(tokenIds), Object.values(tokenIds), '0x'); + }); + + it('can receive ERC1155 safeTransfer', async function () { + await this.token.safeTransferFrom( + other, + this.mock.address, + ...Object.entries(tokenIds)[0], // id + amount + '0x', + { from: other }, + ); + }); + + it('can receive ERC1155 safeBatchTransfer', async function () { + await this.token.safeBatchTransferFrom( + other, + this.mock.address, + Object.keys(tokenIds), + Object.values(tokenIds), + '0x', + { from: other }, + ); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/compatibility/GovernorCompatibilityBravo.test.js b/lib/openzeppelin-contracts/test/governance/compatibility/GovernorCompatibilityBravo.test.js new file mode 100644 index 0000000..7995047 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/compatibility/GovernorCompatibilityBravo.test.js @@ -0,0 +1,265 @@ +const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const RLP = require('rlp'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const Token = artifacts.require('ERC20VotesCompMock'); +const Timelock = artifacts.require('CompTimelock'); +const Governor = artifacts.require('GovernorCompatibilityBravoMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +function makeContractAddress (creator, nonce) { + return web3.utils.toChecksumAddress(web3.utils.sha3(RLP.encode([creator, nonce])).slice(12).substring(14)); +} + +contract('GovernorCompatibilityBravo', function (accounts) { + const [ owner, proposer, voter1, voter2, voter3, voter4, other ] = accounts; + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const proposalThreshold = web3.utils.toWei('10'); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + const [ deployer ] = await web3.eth.getAccounts(); + + this.token = await Token.new(tokenName, tokenSymbol); + + // Need to predict governance address to set it as timelock admin with a delayed transfer + const nonce = await web3.eth.getTransactionCount(deployer); + const predictGovernor = makeContractAddress(deployer, nonce + 1); + + this.timelock = await Timelock.new(predictGovernor, 2 * 86400); + this.mock = await Governor.new( + name, + this.token.address, + votingDelay, + votingPeriod, + proposalThreshold, + this.timelock.address, + ); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.timelock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: proposer, value: proposalThreshold }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + signature: 'mockFunction()', + }, + ], ''); + }); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + expect(await this.mock.quorumVotes()).to.be.bignumber.equal('0'); + expect(await this.mock.COUNTING_MODE()).to.be.equal('support=bravo&quorum=bravo'); + }); + + it('nominal workflow', async function () { + // Before + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(false); + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0'); + expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(value); + expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal('0'); + + // Run proposal + const txPropose = await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For, reason: 'This is nice' }, { from: voter1 }); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }); + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + // After + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true); + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0'); + expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal('0'); + expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(value); + + const proposal = await this.mock.proposals(this.proposal.id); + expect(proposal.id).to.be.bignumber.equal(this.proposal.id); + expect(proposal.proposer).to.be.equal(proposer); + expect(proposal.eta).to.be.bignumber.equal(await this.mock.proposalEta(this.proposal.id)); + expect(proposal.startBlock).to.be.bignumber.equal(await this.mock.proposalSnapshot(this.proposal.id)); + expect(proposal.endBlock).to.be.bignumber.equal(await this.mock.proposalDeadline(this.proposal.id)); + expect(proposal.canceled).to.be.equal(false); + expect(proposal.executed).to.be.equal(true); + + const action = await this.mock.getActions(this.proposal.id); + expect(action.targets).to.be.deep.equal(this.proposal.targets); + // expect(action.values).to.be.deep.equal(this.proposal.values); + expect(action.signatures).to.be.deep.equal(this.proposal.signatures); + expect(action.calldatas).to.be.deep.equal(this.proposal.data); + + const voteReceipt1 = await this.mock.getReceipt(this.proposal.id, voter1); + expect(voteReceipt1.hasVoted).to.be.equal(true); + expect(voteReceipt1.support).to.be.bignumber.equal(Enums.VoteType.For); + expect(voteReceipt1.votes).to.be.bignumber.equal(web3.utils.toWei('10')); + + const voteReceipt2 = await this.mock.getReceipt(this.proposal.id, voter2); + expect(voteReceipt2.hasVoted).to.be.equal(true); + expect(voteReceipt2.support).to.be.bignumber.equal(Enums.VoteType.For); + expect(voteReceipt2.votes).to.be.bignumber.equal(web3.utils.toWei('7')); + + const voteReceipt3 = await this.mock.getReceipt(this.proposal.id, voter3); + expect(voteReceipt3.hasVoted).to.be.equal(true); + expect(voteReceipt3.support).to.be.bignumber.equal(Enums.VoteType.Against); + expect(voteReceipt3.votes).to.be.bignumber.equal(web3.utils.toWei('5')); + + const voteReceipt4 = await this.mock.getReceipt(this.proposal.id, voter4); + expect(voteReceipt4.hasVoted).to.be.equal(true); + expect(voteReceipt4.support).to.be.bignumber.equal(Enums.VoteType.Abstain); + expect(voteReceipt4.votes).to.be.bignumber.equal(web3.utils.toWei('2')); + + expectEvent( + txPropose, + 'ProposalCreated', + { + proposalId: this.proposal.id, + proposer, + targets: this.proposal.targets, + // values: this.proposal.values, + signatures: this.proposal.signatures.map(() => ''), // this event doesn't contain the proposal detail + calldatas: this.proposal.fulldata, + startBlock: new BN(txPropose.receipt.blockNumber).add(votingDelay), + endBlock: new BN(txPropose.receipt.blockNumber).add(votingDelay).add(votingPeriod), + description: this.proposal.description, + }, + ); + expectEvent( + txExecute, + 'ProposalExecuted', + { proposalId: this.proposal.id }, + ); + await expectEvent.inTransaction( + txExecute.tx, + this.receiver, + 'MockFunctionCalled', + ); + }); + + it('double voting is forbiden', async function () { + await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await expectRevert( + this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'GovernorCompatibilityBravo: vote already cast', + ); + }); + + it('with function selector and arguments', async function () { + const target = this.receiver.address; + this.helper.setProposal([ + { target, data: this.receiver.contract.methods.mockFunction().encodeABI() }, + { target, data: this.receiver.contract.methods.mockFunctionWithArgs(17, 42).encodeABI() }, + { target, signature: 'mockFunctionNonPayable()' }, + { + target, + signature: 'mockFunctionWithArgs(uint256,uint256)', + data: web3.eth.abi.encodeParameters(['uint256', 'uint256'], [18, 43]), + }, + ], ''); + + await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For, reason: 'This is nice' }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + await expectEvent.inTransaction( + txExecute.tx, + this.receiver, + 'MockFunctionCalled', + ); + await expectEvent.inTransaction( + txExecute.tx, + this.receiver, + 'MockFunctionCalled', + ); + await expectEvent.inTransaction( + txExecute.tx, + this.receiver, + 'MockFunctionCalledWithArgs', + { a: '17', b: '42' }, + ); + await expectEvent.inTransaction( + txExecute.tx, + this.receiver, + 'MockFunctionCalledWithArgs', + { a: '18', b: '43' }, + ); + }); + + describe('should revert', function () { + describe('on propose', function () { + it('if proposal does not meet proposalThreshold', async function () { + await expectRevert( + this.helper.propose({ from: other }), + 'Governor: proposer votes below proposal threshold', + ); + }); + }); + + describe('on vote', function () { + it('if vote type is invalide', async function () { + await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + await expectRevert( + this.helper.vote({ support: 5 }, { from: voter1 }), + 'GovernorCompatibilityBravo: invalid vote type', + ); + }); + }); + }); + + describe('cancel', function () { + it('proposer can cancel', async function () { + await this.helper.propose({ from: proposer }); + await this.helper.cancel({ from: proposer }); + }); + + it('anyone can cancel if proposer drop below threshold', async function () { + await this.helper.propose({ from: proposer }); + await this.token.transfer(voter1, web3.utils.toWei('1'), { from: proposer }); + await this.helper.cancel(); + }); + + it('cannot cancel is proposer is still above threshold', async function () { + await this.helper.propose({ from: proposer }); + await expectRevert(this.helper.cancel(), 'GovernorBravo: proposer above threshold'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorComp.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorComp.test.js new file mode 100644 index 0000000..06d2d62 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorComp.test.js @@ -0,0 +1,78 @@ +const { BN } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const Token = artifacts.require('ERC20VotesCompMock'); +const Governor = artifacts.require('GovernorCompMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +contract('GovernorComp', function (accounts) { + const [ owner, voter1, voter2, voter3, voter4 ] = accounts; + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + this.owner = owner; + this.token = await Token.new(tokenName, tokenSymbol); + this.mock = await Governor.new(name, this.token.address); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + }); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + }); + + it('voting with comp token', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }); + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter3)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter4)).to.be.equal(true); + + await this.mock.proposalVotes(this.proposal.id).then(results => { + expect(results.forVotes).to.be.bignumber.equal(web3.utils.toWei('17')); + expect(results.againstVotes).to.be.bignumber.equal(web3.utils.toWei('5')); + expect(results.abstainVotes).to.be.bignumber.equal(web3.utils.toWei('2')); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorERC721.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorERC721.test.js new file mode 100644 index 0000000..086fca4 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorERC721.test.js @@ -0,0 +1,104 @@ +const { BN, expectEvent } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const Token = artifacts.require('ERC721VotesMock'); +const Governor = artifacts.require('GovernorVoteMocks'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +contract('GovernorERC721Mock', function (accounts) { + const [ owner, voter1, voter2, voter3, voter4 ] = accounts; + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockNFToken'; + const tokenSymbol = 'MTKN'; + const NFT0 = new BN(0); + const NFT1 = new BN(1); + const NFT2 = new BN(2); + const NFT3 = new BN(3); + const NFT4 = new BN(4); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + this.owner = owner; + this.token = await Token.new(tokenName, tokenSymbol); + this.mock = await Governor.new(name, this.token.address); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value }); + + await Promise.all([ NFT0, NFT1, NFT2, NFT3, NFT4 ].map(tokenId => this.token.mint(owner, tokenId))); + await this.helper.delegate({ token: this.token, to: voter1, tokenId: NFT0 }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, tokenId: NFT1 }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, tokenId: NFT2 }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, tokenId: NFT3 }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, tokenId: NFT4 }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + }); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + }); + + it('voting with ERC721 token', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }), + 'VoteCast', + { voter: voter1, support: Enums.VoteType.For, weight: '1' }, + ); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }), + 'VoteCast', + { voter: voter2, support: Enums.VoteType.For, weight: '2' }, + ); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }), + 'VoteCast', + { voter: voter3, support: Enums.VoteType.Against, weight: '1' }, + ); + + expectEvent( + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }), + 'VoteCast', + { voter: voter4, support: Enums.VoteType.Abstain, weight: '1' }, + ); + + await this.helper.waitForDeadline(); + await this.helper.execute(); + + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter3)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter4)).to.be.equal(true); + + await this.mock.proposalVotes(this.proposal.id).then(results => { + expect(results.forVotes).to.be.bignumber.equal('3'); + expect(results.againstVotes).to.be.bignumber.equal('1'); + expect(results.abstainVotes).to.be.bignumber.equal('1'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorPreventLateQuorum.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorPreventLateQuorum.test.js new file mode 100644 index 0000000..6a5d644 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorPreventLateQuorum.test.js @@ -0,0 +1,177 @@ +const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const Token = artifacts.require('ERC20VotesCompMock'); +const Governor = artifacts.require('GovernorPreventLateQuorumMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +contract('GovernorPreventLateQuorum', function (accounts) { + const [ owner, proposer, voter1, voter2, voter3, voter4 ] = accounts; + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const lateQuorumVoteExtension = new BN(8); + const quorum = web3.utils.toWei('1'); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + this.owner = owner; + this.token = await Token.new(tokenName, tokenSymbol); + this.mock = await Governor.new( + name, + this.token.address, + votingDelay, + votingPeriod, + quorum, + lateQuorumVoteExtension, + ); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + }); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal(quorum); + expect(await this.mock.lateQuorumVoteExtension()).to.be.bignumber.equal(lateQuorumVoteExtension); + }); + + it('nominal workflow unaffected', async function () { + const txPropose = await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }); + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter3)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter4)).to.be.equal(true); + + await this.mock.proposalVotes(this.proposal.id).then(results => { + expect(results.forVotes).to.be.bignumber.equal(web3.utils.toWei('17')); + expect(results.againstVotes).to.be.bignumber.equal(web3.utils.toWei('5')); + expect(results.abstainVotes).to.be.bignumber.equal(web3.utils.toWei('2')); + }); + + const startBlock = new BN(txPropose.receipt.blockNumber).add(votingDelay); + const endBlock = new BN(txPropose.receipt.blockNumber).add(votingDelay).add(votingPeriod); + expect(await this.mock.proposalSnapshot(this.proposal.id)).to.be.bignumber.equal(startBlock); + expect(await this.mock.proposalDeadline(this.proposal.id)).to.be.bignumber.equal(endBlock); + + expectEvent( + txPropose, + 'ProposalCreated', + { + proposalId: this.proposal.id, + proposer, + targets: this.proposal.targets, + // values: this.proposal.values.map(value => new BN(value)), + signatures: this.proposal.signatures, + calldatas: this.proposal.data, + startBlock, + endBlock, + description: this.proposal.description, + }, + ); + }); + + it('Delay is extended to prevent last minute take-over', async function () { + const txPropose = await this.helper.propose({ from: proposer }); + + // compute original schedule + const startBlock = new BN(txPropose.receipt.blockNumber).add(votingDelay); + const endBlock = new BN(txPropose.receipt.blockNumber).add(votingDelay).add(votingPeriod); + expect(await this.mock.proposalSnapshot(this.proposal.id)).to.be.bignumber.equal(startBlock); + expect(await this.mock.proposalDeadline(this.proposal.id)).to.be.bignumber.equal(endBlock); + + // wait for the last minute to vote + await this.helper.waitForDeadline(-1); + const txVote = await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + + // cannot execute yet + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active); + + // compute new extended schedule + const extendedDeadline = new BN(txVote.receipt.blockNumber).add(lateQuorumVoteExtension); + expect(await this.mock.proposalSnapshot(this.proposal.id)).to.be.bignumber.equal(startBlock); + expect(await this.mock.proposalDeadline(this.proposal.id)).to.be.bignumber.equal(extendedDeadline); + + // still possible to vote + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter1 }); + + await this.helper.waitForDeadline(); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Active); + await this.helper.waitForDeadline(+1); + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Defeated); + + // check extension event + expectEvent( + txVote, + 'ProposalExtended', + { proposalId: this.proposal.id, extendedDeadline }, + ); + }); + + describe('onlyGovernance updates', function () { + it('setLateQuorumVoteExtension is protected', async function () { + await expectRevert( + this.mock.setLateQuorumVoteExtension(0), + 'Governor: onlyGovernance', + ); + }); + + it('can setLateQuorumVoteExtension through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.setLateQuorumVoteExtension('0').encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.execute(), + 'LateQuorumVoteExtensionSet', + { oldVoteExtension: lateQuorumVoteExtension, newVoteExtension: '0' }, + ); + + expect(await this.mock.lateQuorumVoteExtension()).to.be.bignumber.equal('0'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockCompound.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockCompound.test.js new file mode 100644 index 0000000..a31df68 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockCompound.test.js @@ -0,0 +1,368 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const RLP = require('rlp'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const { + shouldSupportInterfaces, +} = require('../../utils/introspection/SupportsInterface.behavior'); + +const Token = artifacts.require('ERC20VotesMock'); +const Timelock = artifacts.require('CompTimelock'); +const Governor = artifacts.require('GovernorTimelockCompoundMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +function makeContractAddress (creator, nonce) { + return web3.utils.toChecksumAddress(web3.utils.sha3(RLP.encode([creator, nonce])).slice(12).substring(14)); +} + +contract('GovernorTimelockCompound', function (accounts) { + const [ owner, voter1, voter2, voter3, voter4, other ] = accounts; + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + const [ deployer ] = await web3.eth.getAccounts(); + + this.token = await Token.new(tokenName, tokenSymbol); + + // Need to predict governance address to set it as timelock admin with a delayed transfer + const nonce = await web3.eth.getTransactionCount(deployer); + const predictGovernor = makeContractAddress(deployer, nonce + 1); + + this.timelock = await Timelock.new(predictGovernor, 2 * 86400); + this.mock = await Governor.new( + name, + this.token.address, + votingDelay, + votingPeriod, + this.timelock.address, + 0, + ); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.timelock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + }); + + shouldSupportInterfaces([ + 'ERC165', + 'Governor', + 'GovernorWithParams', + 'GovernorTimelock', + ]); + + it('doesn\'t accept ether transfers', async function () { + await expectRevert.unspecified(web3.eth.sendTransaction({ from: owner, to: this.mock.address, value: 1 })); + }); + + it('post deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + + expect(await this.mock.timelock()).to.be.equal(this.timelock.address); + expect(await this.timelock.admin()).to.be.equal(this.mock.address); + }); + + it('nominal', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }); + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }); + await this.helper.waitForDeadline(); + const txQueue = await this.helper.queue(); + const eta = await this.mock.proposalEta(this.proposal.id); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + expectEvent(txQueue, 'ProposalQueued', { proposalId: this.proposal.id }); + await expectEvent.inTransaction(txQueue.tx, this.timelock, 'QueueTransaction', { eta }); + + expectEvent(txExecute, 'ProposalExecuted', { proposalId: this.proposal.id }); + await expectEvent.inTransaction(txExecute.tx, this.timelock, 'ExecuteTransaction', { eta }); + await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalled'); + }); + + describe('should revert', function () { + describe('on queue', function () { + it('if already queued', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await expectRevert(this.helper.queue(), 'Governor: proposal not successful'); + }); + + it('if proposal contains duplicate calls', async function () { + const action = { + target: this.token.address, + data: this.token.contract.methods.approve(this.receiver.address, constants.MAX_UINT256).encodeABI(), + }; + this.helper.setProposal([ action, action ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await expectRevert( + this.helper.queue(), + 'GovernorTimelockCompound: identical proposal action already queued', + ); + await expectRevert( + this.helper.execute(), + 'GovernorTimelockCompound: proposal not yet queued', + ); + }); + }); + + describe('on execute', function () { + it('if not queued', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(+1); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded); + + await expectRevert( + this.helper.execute(), + 'GovernorTimelockCompound: proposal not yet queued', + ); + }); + + it('if too early', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued); + + await expectRevert( + this.helper.execute(), + 'Timelock::executeTransaction: Transaction hasn\'t surpassed time lock', + ); + }); + + it('if too late', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(+30 * 86400); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Expired); + + await expectRevert( + this.helper.execute(), + 'Governor: proposal not successful', + ); + }); + + it('if already executed', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + await this.helper.execute(); + await expectRevert( + this.helper.execute(), + 'Governor: proposal not successful', + ); + }); + }); + }); + + describe('cancel', function () { + it('cancel before queue prevents scheduling', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.cancel(), + 'ProposalCanceled', + { proposalId: this.proposal.id }, + ); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + await expectRevert(this.helper.queue(), 'Governor: proposal not successful'); + }); + + it('cancel after queue prevents executing', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + + expectEvent( + await this.helper.cancel(), + 'ProposalCanceled', + { proposalId: this.proposal.id }, + ); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + }); + + describe('onlyGovernance', function () { + describe('relay', function () { + beforeEach(async function () { + await this.token.mint(this.mock.address, 1); + }); + + it('is protected', async function () { + await expectRevert( + this.mock.relay( + this.token.address, + 0, + this.token.contract.methods.transfer(other, 1).encodeABI(), + ), + 'Governor: onlyGovernance', + ); + }); + + it('can be executed through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.relay( + this.token.address, + 0, + this.token.contract.methods.transfer(other, 1).encodeABI(), + ).encodeABI(), + }, + ], ''); + + expect(await this.token.balanceOf(this.mock.address), 1); + expect(await this.token.balanceOf(other), 0); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + expect(await this.token.balanceOf(this.mock.address), 0); + expect(await this.token.balanceOf(other), 1); + + expectEvent.inTransaction( + txExecute.tx, + this.token, + 'Transfer', + { from: this.mock.address, to: other, value: '1' }, + ); + }); + }); + + describe('updateTimelock', function () { + beforeEach(async function () { + this.newTimelock = await Timelock.new(this.mock.address, 7 * 86400); + }); + + it('is protected', async function () { + await expectRevert( + this.mock.updateTimelock(this.newTimelock.address), + 'Governor: onlyGovernance', + ); + }); + + it('can be executed through governance to', async function () { + this.helper.setProposal([ + { + target: this.timelock.address, + data: this.timelock.contract.methods.setPendingAdmin(owner).encodeABI(), + }, + { + target: this.mock.address, + data: this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + expectEvent( + txExecute, + 'TimelockChange', + { oldTimelock: this.timelock.address, newTimelock: this.newTimelock.address }, + ); + + expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address); + }); + }); + + it('can transfer timelock to new governor', async function () { + const newGovernor = await Governor.new(name, this.token.address, 8, 32, this.timelock.address, 0); + + this.helper.setProposal([ + { + target: this.timelock.address, + data: this.timelock.contract.methods.setPendingAdmin(newGovernor.address).encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + await expectEvent.inTransaction( + txExecute.tx, + this.timelock, + 'NewPendingAdmin', + { newPendingAdmin: newGovernor.address }, + ); + + await newGovernor.__acceptAdmin(); + expect(await this.timelock.admin()).to.be.bignumber.equal(newGovernor.address); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockControl.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockControl.test.js new file mode 100644 index 0000000..56d3b22 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorTimelockControl.test.js @@ -0,0 +1,420 @@ +const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const { + shouldSupportInterfaces, +} = require('../../utils/introspection/SupportsInterface.behavior'); + +const Token = artifacts.require('ERC20VotesMock'); +const Timelock = artifacts.require('TimelockController'); +const Governor = artifacts.require('GovernorTimelockControlMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +contract('GovernorTimelockControl', function (accounts) { + const [ owner, voter1, voter2, voter3, voter4, other ] = accounts; + + const TIMELOCK_ADMIN_ROLE = web3.utils.soliditySha3('TIMELOCK_ADMIN_ROLE'); + const PROPOSER_ROLE = web3.utils.soliditySha3('PROPOSER_ROLE'); + const EXECUTOR_ROLE = web3.utils.soliditySha3('EXECUTOR_ROLE'); + const CANCELLER_ROLE = web3.utils.soliditySha3('CANCELLER_ROLE'); + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + const [ deployer ] = await web3.eth.getAccounts(); + + this.token = await Token.new(tokenName, tokenSymbol); + this.timelock = await Timelock.new(3600, [], [], deployer); + this.mock = await Governor.new( + name, + this.token.address, + votingDelay, + votingPeriod, + this.timelock.address, + 0, + ); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + this.TIMELOCK_ADMIN_ROLE = await this.timelock.TIMELOCK_ADMIN_ROLE(); + this.PROPOSER_ROLE = await this.timelock.PROPOSER_ROLE(); + this.EXECUTOR_ROLE = await this.timelock.EXECUTOR_ROLE(); + this.CANCELLER_ROLE = await this.timelock.CANCELLER_ROLE(); + + await web3.eth.sendTransaction({ from: owner, to: this.timelock.address, value }); + + // normal setup: governor is proposer, everyone is executor, timelock is its own admin + await this.timelock.grantRole(PROPOSER_ROLE, this.mock.address); + await this.timelock.grantRole(PROPOSER_ROLE, owner); + await this.timelock.grantRole(CANCELLER_ROLE, this.mock.address); + await this.timelock.grantRole(CANCELLER_ROLE, owner); + await this.timelock.grantRole(EXECUTOR_ROLE, constants.ZERO_ADDRESS); + await this.timelock.revokeRole(TIMELOCK_ADMIN_ROLE, deployer); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + this.proposal.timelockid = await this.timelock.hashOperationBatch( + ...this.proposal.shortProposal.slice(0, 3), + '0x0', + this.proposal.shortProposal[3], + ); + }); + + shouldSupportInterfaces([ + 'ERC165', + 'Governor', + 'GovernorWithParams', + 'GovernorTimelock', + ]); + + it('doesn\'t accept ether transfers', async function () { + await expectRevert.unspecified(web3.eth.sendTransaction({ from: owner, to: this.mock.address, value: 1 })); + }); + + it('post deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + + expect(await this.mock.timelock()).to.be.equal(this.timelock.address); + }); + + it('nominal', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }); + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }); + await this.helper.waitForDeadline(); + const txQueue = await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + expectEvent(txQueue, 'ProposalQueued', { proposalId: this.proposal.id }); + await expectEvent.inTransaction(txQueue.tx, this.timelock, 'CallScheduled', { id: this.proposal.timelockid }); + + expectEvent(txExecute, 'ProposalExecuted', { proposalId: this.proposal.id }); + await expectEvent.inTransaction(txExecute.tx, this.timelock, 'CallExecuted', { id: this.proposal.timelockid }); + await expectEvent.inTransaction(txExecute.tx, this.receiver, 'MockFunctionCalled'); + }); + + describe('should revert', function () { + describe('on queue', function () { + it('if already queued', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await expectRevert(this.helper.queue(), 'Governor: proposal not successful'); + }); + }); + + describe('on execute', function () { + it('if not queued', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(+1); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Succeeded); + + await expectRevert(this.helper.execute(), 'TimelockController: operation is not ready'); + }); + + it('if too early', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued); + + await expectRevert(this.helper.execute(), 'TimelockController: operation is not ready'); + }); + + it('if already executed', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + await this.helper.execute(); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('if already executed by another proposer', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + + await this.timelock.executeBatch( + ...this.proposal.shortProposal.slice(0, 3), + '0x0', + this.proposal.shortProposal[3], + ); + + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + }); + }); + + describe('cancel', function () { + it('cancel before queue prevents scheduling', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.cancel(), + 'ProposalCanceled', + { proposalId: this.proposal.id }, + ); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + await expectRevert(this.helper.queue(), 'Governor: proposal not successful'); + }); + + it('cancel after queue prevents executing', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + + expectEvent( + await this.helper.cancel(), + 'ProposalCanceled', + { proposalId: this.proposal.id }, + ); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + it('cancel on timelock is reflected on governor', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Queued); + + expectEvent( + await this.timelock.cancel(this.proposal.timelockid, { from: owner }), + 'Cancelled', + { id: this.proposal.timelockid }, + ); + + expect(await this.mock.state(this.proposal.id)).to.be.bignumber.equal(Enums.ProposalState.Canceled); + }); + }); + + describe('onlyGovernance', function () { + describe('relay', function () { + beforeEach(async function () { + await this.token.mint(this.mock.address, 1); + }); + + it('is protected', async function () { + await expectRevert( + this.mock.relay( + this.token.address, + 0, + this.token.contract.methods.transfer(other, 1).encodeABI(), + ), + 'Governor: onlyGovernance', + ); + }); + + it('can be executed through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.relay( + this.token.address, + 0, + this.token.contract.methods.transfer(other, 1).encodeABI(), + ).encodeABI(), + }, + ], ''); + + expect(await this.token.balanceOf(this.mock.address), 1); + expect(await this.token.balanceOf(other), 0); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + expect(await this.token.balanceOf(this.mock.address), 0); + expect(await this.token.balanceOf(other), 1); + + expectEvent.inTransaction( + txExecute.tx, + this.token, + 'Transfer', + { from: this.mock.address, to: other, value: '1' }, + ); + }); + + it('is payable and can transfer eth to EOA', async function () { + const t2g = web3.utils.toBN(128); // timelock to governor + const g2o = web3.utils.toBN(100); // governor to eoa (other) + + this.helper.setProposal([ + { + target: this.mock.address, + value: t2g, + data: this.mock.contract.methods.relay( + other, + g2o, + '0x', + ).encodeABI(), + }, + ], ''); + + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(web3.utils.toBN(0)); + const timelockBalance = await web3.eth.getBalance(this.timelock.address).then(web3.utils.toBN); + const otherBalance = await web3.eth.getBalance(other).then(web3.utils.toBN); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + await this.helper.execute(); + + expect(await web3.eth.getBalance(this.timelock.address)).to.be.bignumber.equal(timelockBalance.sub(t2g)); + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal(t2g.sub(g2o)); + expect(await web3.eth.getBalance(other)).to.be.bignumber.equal(otherBalance.add(g2o)); + }); + + it('protected against other proposers', async function () { + await this.timelock.schedule( + this.mock.address, + web3.utils.toWei('0'), + this.mock.contract.methods.relay(constants.ZERO_ADDRESS, 0, '0x').encodeABI(), + constants.ZERO_BYTES32, + constants.ZERO_BYTES32, + 3600, + { from: owner }, + ); + + await time.increase(3600); + + await expectRevert( + this.timelock.execute( + this.mock.address, + web3.utils.toWei('0'), + this.mock.contract.methods.relay(constants.ZERO_ADDRESS, 0, '0x').encodeABI(), + constants.ZERO_BYTES32, + constants.ZERO_BYTES32, + { from: owner }, + ), + 'TimelockController: underlying transaction reverted', + ); + }); + }); + + describe('updateTimelock', function () { + beforeEach(async function () { + this.newTimelock = await Timelock.new( + 3600, + [ this.mock.address ], + [ this.mock.address ], + constants.ZERO_ADDRESS, + ); + }); + + it('is protected', async function () { + await expectRevert( + this.mock.updateTimelock(this.newTimelock.address), + 'Governor: onlyGovernance', + ); + }); + + it('can be executed through governance to', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.updateTimelock(this.newTimelock.address).encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + const txExecute = await this.helper.execute(); + + expectEvent( + txExecute, + 'TimelockChange', + { oldTimelock: this.timelock.address, newTimelock: this.newTimelock.address }, + ); + + expect(await this.mock.timelock()).to.be.bignumber.equal(this.newTimelock.address); + }); + }); + }); + + it('clear queue of pending governor calls', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.nonGovernanceFunction().encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.queue(); + await this.helper.waitForEta(); + await this.helper.execute(); + + // This path clears _governanceCall as part of the afterExecute call, + // but we have not way to check that the cleanup actually happened other + // then coverage reports. + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorVotesQuorumFraction.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorVotesQuorumFraction.test.js new file mode 100644 index 0000000..9e6b71b --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorVotesQuorumFraction.test.js @@ -0,0 +1,137 @@ +const { BN, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const Enums = require('../../helpers/enums'); +const { GovernorHelper } = require('../../helpers/governance'); + +const Token = artifacts.require('ERC20VotesMock'); +const Governor = artifacts.require('GovernorMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +contract('GovernorVotesQuorumFraction', function (accounts) { + const [ owner, voter1, voter2, voter3, voter4 ] = accounts; + + const name = 'OZ-Governor'; + // const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = new BN(web3.utils.toWei('100')); + const ratio = new BN(8); // percents + const newRatio = new BN(6); // percents + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + this.owner = owner; + this.token = await Token.new(tokenName, tokenSymbol); + this.mock = await Governor.new(name, this.token.address, votingDelay, votingPeriod, ratio); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + }); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + expect(await this.mock.quorum(0)).to.be.bignumber.equal('0'); + expect(await this.mock.quorumNumerator()).to.be.bignumber.equal(ratio); + expect(await this.mock.quorumDenominator()).to.be.bignumber.equal('100'); + expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1)))) + .to.be.bignumber.equal(tokenSupply.mul(ratio).divn(100)); + }); + + it('quroum reached', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + }); + + it('quroum not reached', async function () { + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.waitForDeadline(); + await expectRevert(this.helper.execute(), 'Governor: proposal not successful'); + }); + + describe('onlyGovernance updates', function () { + it('updateQuorumNumerator is protected', async function () { + await expectRevert( + this.mock.updateQuorumNumerator(newRatio), + 'Governor: onlyGovernance', + ); + }); + + it('can updateQuorumNumerator through governance', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.updateQuorumNumerator(newRatio).encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + expectEvent( + await this.helper.execute(), + 'QuorumNumeratorUpdated', + { oldQuorumNumerator: ratio, newQuorumNumerator: newRatio }, + ); + + expect(await this.mock.quorumNumerator()).to.be.bignumber.equal(newRatio); + expect(await this.mock.quorumDenominator()).to.be.bignumber.equal('100'); + + // it takes one block for the new quorum to take effect + expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1)))) + .to.be.bignumber.equal(tokenSupply.mul(ratio).divn(100)); + + await time.advanceBlock(); + + expect(await time.latestBlock().then(blockNumber => this.mock.quorum(blockNumber.subn(1)))) + .to.be.bignumber.equal(tokenSupply.mul(newRatio).divn(100)); + }); + + it('cannot updateQuorumNumerator over the maximum', async function () { + this.helper.setProposal([ + { + target: this.mock.address, + data: this.mock.contract.methods.updateQuorumNumerator('101').encodeABI(), + }, + ], ''); + + await this.helper.propose(); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter1 }); + await this.helper.waitForDeadline(); + + await expectRevert( + this.helper.execute(), + 'GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/extensions/GovernorWithParams.test.js b/lib/openzeppelin-contracts/test/governance/extensions/GovernorWithParams.test.js new file mode 100644 index 0000000..875b705 --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/extensions/GovernorWithParams.test.js @@ -0,0 +1,166 @@ +const { BN, expectEvent } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; +const { fromRpcSig } = require('ethereumjs-util'); +const Enums = require('../../helpers/enums'); +const { EIP712Domain } = require('../../helpers/eip712'); +const { GovernorHelper } = require('../../helpers/governance'); + +const Token = artifacts.require('ERC20VotesCompMock'); +const Governor = artifacts.require('GovernorWithParamsMock'); +const CallReceiver = artifacts.require('CallReceiverMock'); + +const rawParams = { + uintParam: new BN('42'), + strParam: 'These are my params', +}; + +const encodedParams = web3.eth.abi.encodeParameters( + [ 'uint256', 'string' ], + Object.values(rawParams), +); + +contract('GovernorWithParams', function (accounts) { + const [ owner, proposer, voter1, voter2, voter3, voter4 ] = accounts; + + const name = 'OZ-Governor'; + const version = '1'; + const tokenName = 'MockToken'; + const tokenSymbol = 'MTKN'; + const tokenSupply = web3.utils.toWei('100'); + const votingDelay = new BN(4); + const votingPeriod = new BN(16); + const value = web3.utils.toWei('1'); + + beforeEach(async function () { + this.chainId = await web3.eth.getChainId(); + this.token = await Token.new(tokenName, tokenSymbol); + this.mock = await Governor.new(name, this.token.address); + this.receiver = await CallReceiver.new(); + + this.helper = new GovernorHelper(this.mock); + + await web3.eth.sendTransaction({ from: owner, to: this.mock.address, value }); + + await this.token.mint(owner, tokenSupply); + await this.helper.delegate({ token: this.token, to: voter1, value: web3.utils.toWei('10') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter2, value: web3.utils.toWei('7') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter3, value: web3.utils.toWei('5') }, { from: owner }); + await this.helper.delegate({ token: this.token, to: voter4, value: web3.utils.toWei('2') }, { from: owner }); + + // default proposal + this.proposal = this.helper.setProposal([ + { + target: this.receiver.address, + value, + data: this.receiver.contract.methods.mockFunction().encodeABI(), + }, + ], ''); + }); + + it('deployment check', async function () { + expect(await this.mock.name()).to.be.equal(name); + expect(await this.mock.token()).to.be.equal(this.token.address); + expect(await this.mock.votingDelay()).to.be.bignumber.equal(votingDelay); + expect(await this.mock.votingPeriod()).to.be.bignumber.equal(votingPeriod); + }); + + it('nominal is unaffected', async function () { + await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + await this.helper.vote({ support: Enums.VoteType.For, reason: 'This is nice' }, { from: voter1 }); + await this.helper.vote({ support: Enums.VoteType.For }, { from: voter2 }); + await this.helper.vote({ support: Enums.VoteType.Against }, { from: voter3 }); + await this.helper.vote({ support: Enums.VoteType.Abstain }, { from: voter4 }); + await this.helper.waitForDeadline(); + await this.helper.execute(); + + expect(await this.mock.hasVoted(this.proposal.id, owner)).to.be.equal(false); + expect(await this.mock.hasVoted(this.proposal.id, voter1)).to.be.equal(true); + expect(await this.mock.hasVoted(this.proposal.id, voter2)).to.be.equal(true); + expect(await web3.eth.getBalance(this.mock.address)).to.be.bignumber.equal('0'); + expect(await web3.eth.getBalance(this.receiver.address)).to.be.bignumber.equal(value); + }); + + it('Voting with params is properly supported', async function () { + await this.helper.propose({ from: proposer }); + await this.helper.waitForSnapshot(); + + const weight = new BN(web3.utils.toWei('7')).sub(rawParams.uintParam); + + const tx = await this.helper.vote({ + support: Enums.VoteType.For, + reason: 'no particular reason', + params: encodedParams, + }, { from: voter2 }); + + expectEvent(tx, 'CountParams', { ...rawParams }); + expectEvent(tx, 'VoteCastWithParams', { + voter: voter2, + proposalId: this.proposal.id, + support: Enums.VoteType.For, + weight, + reason: 'no particular reason', + params: encodedParams, + }); + + const votes = await this.mock.proposalVotes(this.proposal.id); + expect(votes.forVotes).to.be.bignumber.equal(weight); + }); + + it('Voting with params by signature is properly supported', async function () { + const voterBySig = Wallet.generate(); + const voterBySigAddress = web3.utils.toChecksumAddress(voterBySig.getAddressString()); + + const signature = async (message) => { + return fromRpcSig(ethSigUtil.signTypedMessage( + voterBySig.getPrivateKey(), + { + data: { + types: { + EIP712Domain, + ExtendedBallot: [ + { name: 'proposalId', type: 'uint256' }, + { name: 'support', type: 'uint8' }, + { name: 'reason', type: 'string' }, + { name: 'params', type: 'bytes' }, + ], + }, + domain: { name, version, chainId: this.chainId, verifyingContract: this.mock.address }, + primaryType: 'ExtendedBallot', + message, + }, + }, + )); + }; + + await this.token.delegate(voterBySigAddress, { from: voter2 }); + + // Run proposal + await this.helper.propose(); + await this.helper.waitForSnapshot(); + + const weight = new BN(web3.utils.toWei('7')).sub(rawParams.uintParam); + + const tx = await this.helper.vote({ + support: Enums.VoteType.For, + reason: 'no particular reason', + params: encodedParams, + signature, + }); + + expectEvent(tx, 'CountParams', { ...rawParams }); + expectEvent(tx, 'VoteCastWithParams', { + voter: voterBySigAddress, + proposalId: this.proposal.id, + support: Enums.VoteType.For, + weight, + reason: 'no particular reason', + params: encodedParams, + }); + + const votes = await this.mock.proposalVotes(this.proposal.id); + expect(votes.forVotes).to.be.bignumber.equal(weight); + }); +}); diff --git a/lib/openzeppelin-contracts/test/governance/utils/Votes.behavior.js b/lib/openzeppelin-contracts/test/governance/utils/Votes.behavior.js new file mode 100644 index 0000000..aee227b --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/utils/Votes.behavior.js @@ -0,0 +1,344 @@ +const { constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); + +const { MAX_UINT256, ZERO_ADDRESS } = constants; + +const { fromRpcSig } = require('ethereumjs-util'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; + +const { EIP712Domain, domainSeparator } = require('../../helpers/eip712'); + +const Delegation = [ + { name: 'delegatee', type: 'address' }, + { name: 'nonce', type: 'uint256' }, + { name: 'expiry', type: 'uint256' }, +]; + +const version = '1'; + +function shouldBehaveLikeVotes () { + describe('run votes workflow', function () { + it('initial nonce is 0', async function () { + expect(await this.votes.nonces(this.account1)).to.be.bignumber.equal('0'); + }); + + it('domain separator', async function () { + expect( + await this.votes.DOMAIN_SEPARATOR(), + ).to.equal( + await domainSeparator(this.name, version, this.chainId, this.votes.address), + ); + }); + + describe('delegation with signature', function () { + const delegator = Wallet.generate(); + const delegatorAddress = web3.utils.toChecksumAddress(delegator.getAddressString()); + const nonce = 0; + + const buildData = (chainId, verifyingContract, name, message) => ({ + data: { + primaryType: 'Delegation', + types: { EIP712Domain, Delegation }, + domain: { name, version, chainId, verifyingContract }, + message, + }, + }); + + beforeEach(async function () { + await this.votes.mint(delegatorAddress, this.NFT0); + }); + + it('accept signed delegation', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.votes.address, this.name, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + expect(await this.votes.delegates(delegatorAddress)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s); + expectEvent(receipt, 'DelegateChanged', { + delegator: delegatorAddress, + fromDelegate: ZERO_ADDRESS, + toDelegate: delegatorAddress, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: delegatorAddress, + previousBalance: '0', + newBalance: '1', + }); + + expect(await this.votes.delegates(delegatorAddress)).to.be.equal(delegatorAddress); + + expect(await this.votes.getVotes(delegatorAddress)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastVotes(delegatorAddress, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.votes.getPastVotes(delegatorAddress, receipt.blockNumber)).to.be.bignumber.equal('1'); + }); + + it('rejects reused signature', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.votes.address, this.name, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + await this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s); + + await expectRevert( + this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s), + 'Votes: invalid nonce', + ); + }); + + it('rejects bad delegatee', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.votes.address, this.name, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + const receipt = await this.votes.delegateBySig(this.account1Delegatee, nonce, MAX_UINT256, v, r, s); + const { args } = receipt.logs.find(({ event }) => event === 'DelegateChanged'); + expect(args.delegator).to.not.be.equal(delegatorAddress); + expect(args.fromDelegate).to.be.equal(ZERO_ADDRESS); + expect(args.toDelegate).to.be.equal(this.account1Delegatee); + }); + + it('rejects bad nonce', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.votes.address, this.name, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + await expectRevert( + this.votes.delegateBySig(delegatorAddress, nonce + 1, MAX_UINT256, v, r, s), + 'Votes: invalid nonce', + ); + }); + + it('rejects expired permit', async function () { + const expiry = (await time.latest()) - time.duration.weeks(1); + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.votes.address, this.name, { + delegatee: delegatorAddress, + nonce, + expiry, + }), + )); + + await expectRevert( + this.votes.delegateBySig(delegatorAddress, nonce, expiry, v, r, s), + 'Votes: signature expired', + ); + }); + }); + + describe('set delegation', function () { + describe('call', function () { + it('delegation with tokens', async function () { + await this.votes.mint(this.account1, this.NFT0); + expect(await this.votes.delegates(this.account1)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.votes.delegate(this.account1, { from: this.account1 }); + expectEvent(receipt, 'DelegateChanged', { + delegator: this.account1, + fromDelegate: ZERO_ADDRESS, + toDelegate: this.account1, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: this.account1, + previousBalance: '0', + newBalance: '1', + }); + + expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1); + + expect(await this.votes.getVotes(this.account1)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber)).to.be.bignumber.equal('1'); + }); + + it('delegation without tokens', async function () { + expect(await this.votes.delegates(this.account1)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.votes.delegate(this.account1, { from: this.account1 }); + expectEvent(receipt, 'DelegateChanged', { + delegator: this.account1, + fromDelegate: ZERO_ADDRESS, + toDelegate: this.account1, + }); + expectEvent.notEmitted(receipt, 'DelegateVotesChanged'); + + expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1); + }); + }); + }); + + describe('change delegation', function () { + beforeEach(async function () { + await this.votes.mint(this.account1, this.NFT0); + await this.votes.delegate(this.account1, { from: this.account1 }); + }); + + it('call', async function () { + expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1); + + const { receipt } = await this.votes.delegate(this.account1Delegatee, { from: this.account1 }); + expectEvent(receipt, 'DelegateChanged', { + delegator: this.account1, + fromDelegate: this.account1, + toDelegate: this.account1Delegatee, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: this.account1, + previousBalance: '1', + newBalance: '0', + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: this.account1Delegatee, + previousBalance: '0', + newBalance: '1', + }); + const prevBlock = receipt.blockNumber - 1; + expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1Delegatee); + + expect(await this.votes.getVotes(this.account1)).to.be.bignumber.equal('0'); + expect(await this.votes.getVotes(this.account1Delegatee)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber - 1)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastVotes(this.account1Delegatee, prevBlock)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastVotes(this.account1Delegatee, receipt.blockNumber)).to.be.bignumber.equal('1'); + }); + }); + + describe('getPastTotalSupply', function () { + beforeEach(async function () { + await this.votes.delegate(this.account1, { from: this.account1 }); + }); + + it('reverts if block number >= current block', async function () { + await expectRevert( + this.votes.getPastTotalSupply(5e10), + 'block not yet mined', + ); + }); + + it('returns 0 if there are no checkpoints', async function () { + expect(await this.votes.getPastTotalSupply(0)).to.be.bignumber.equal('0'); + }); + + it('returns the latest block if >= last checkpoint block', async function () { + const t1 = await this.votes.mint(this.account1, this.NFT0); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('1'); + }); + + it('returns zero if < first checkpoint block', async function () { + await time.advanceBlock(); + const t2 = await this.votes.mint(this.account1, this.NFT1); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('1'); + }); + + it('generally returns the voting balance at the appropriate checkpoint', async function () { + const t1 = await this.votes.mint(this.account1, this.NFT1); + await time.advanceBlock(); + await time.advanceBlock(); + const t2 = await this.votes.burn(this.NFT1); + await time.advanceBlock(); + await time.advanceBlock(); + const t3 = await this.votes.mint(this.account1, this.NFT2); + await time.advanceBlock(); + await time.advanceBlock(); + const t4 = await this.votes.burn(this.NFT2); + await time.advanceBlock(); + await time.advanceBlock(); + const t5 = await this.votes.mint(this.account1, this.NFT3); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t3.receipt.blockNumber)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastTotalSupply(t3.receipt.blockNumber + 1)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastTotalSupply(t4.receipt.blockNumber)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t4.receipt.blockNumber + 1)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastTotalSupply(t5.receipt.blockNumber)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastTotalSupply(t5.receipt.blockNumber + 1)).to.be.bignumber.equal('1'); + }); + }); + + // The following tests are a adaptation of + // https://github.com/compound-finance/compound-protocol/blob/master/tests/Governance/CompTest.js. + describe('Compound test suite', function () { + beforeEach(async function () { + await this.votes.mint(this.account1, this.NFT0); + await this.votes.mint(this.account1, this.NFT1); + await this.votes.mint(this.account1, this.NFT2); + await this.votes.mint(this.account1, this.NFT3); + }); + + describe('getPastVotes', function () { + it('reverts if block number >= current block', async function () { + await expectRevert( + this.votes.getPastVotes(this.account2, 5e10), + 'block not yet mined', + ); + }); + + it('returns 0 if there are no checkpoints', async function () { + expect(await this.votes.getPastVotes(this.account2, 0)).to.be.bignumber.equal('0'); + }); + + it('returns the latest block if >= last checkpoint block', async function () { + const t1 = await this.votes.delegate(this.account2, { from: this.account1 }); + await time.advanceBlock(); + await time.advanceBlock(); + const latest = await this.votes.getVotes(this.account2); + const nextBlock = t1.receipt.blockNumber + 1; + expect(await this.votes.getPastVotes(this.account2, t1.receipt.blockNumber)).to.be.bignumber.equal(latest); + expect(await this.votes.getPastVotes(this.account2, nextBlock)).to.be.bignumber.equal(latest); + }); + + it('returns zero if < first checkpoint block', async function () { + await time.advanceBlock(); + const t1 = await this.votes.delegate(this.account2, { from: this.account1 }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.votes.getPastVotes(this.account2, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + }); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeVotes, +}; diff --git a/lib/openzeppelin-contracts/test/governance/utils/Votes.test.js b/lib/openzeppelin-contracts/test/governance/utils/Votes.test.js new file mode 100644 index 0000000..32b7d1d --- /dev/null +++ b/lib/openzeppelin-contracts/test/governance/utils/Votes.test.js @@ -0,0 +1,61 @@ +const { expectRevert, BN } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const { + shouldBehaveLikeVotes, +} = require('./Votes.behavior'); + +const Votes = artifacts.require('VotesMock'); + +contract('Votes', function (accounts) { + const [ account1, account2, account3 ] = accounts; + beforeEach(async function () { + this.name = 'My Vote'; + this.votes = await Votes.new(this.name); + }); + + it('starts with zero votes', async function () { + expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('0'); + }); + + describe('performs voting operations', function () { + beforeEach(async function () { + this.tx1 = await this.votes.mint(account1, 1); + this.tx2 = await this.votes.mint(account2, 1); + this.tx3 = await this.votes.mint(account3, 1); + }); + + it('reverts if block number >= current block', async function () { + await expectRevert( + this.votes.getPastTotalSupply(this.tx3.receipt.blockNumber + 1), + 'Votes: block not yet mined', + ); + }); + + it('delegates', async function () { + await this.votes.delegate(account3, account2); + + expect(await this.votes.delegates(account3)).to.be.equal(account2); + }); + + it('returns total amount of votes', async function () { + expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('3'); + }); + }); + + describe('performs voting workflow', function () { + beforeEach(async function () { + this.chainId = await this.votes.getChainId(); + this.account1 = account1; + this.account2 = account2; + this.account1Delegatee = account2; + this.NFT0 = new BN('10000000000000000000000000'); + this.NFT1 = new BN('10'); + this.NFT2 = new BN('20'); + this.NFT3 = new BN('30'); + }); + + shouldBehaveLikeVotes(); + }); +}); diff --git a/lib/openzeppelin-contracts/test/helpers/create2.js b/lib/openzeppelin-contracts/test/helpers/create2.js new file mode 100644 index 0000000..3e57764 --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/create2.js @@ -0,0 +1,12 @@ +function computeCreate2Address (saltHex, bytecode, deployer) { + return web3.utils.toChecksumAddress(`0x${web3.utils.sha3(`0x${[ + 'ff', + deployer, + saltHex, + web3.utils.soliditySha3(bytecode), + ].map(x => x.replace(/0x/, '')).join('')}`).slice(-40)}`); +} + +module.exports = { + computeCreate2Address, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/crosschain.js b/lib/openzeppelin-contracts/test/helpers/crosschain.js new file mode 100644 index 0000000..d4d25d1 --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/crosschain.js @@ -0,0 +1,63 @@ +const { promisify } = require('util'); + +const BridgeAMBMock = artifacts.require('BridgeAMBMock'); +const BridgeArbitrumL1Mock = artifacts.require('BridgeArbitrumL1Mock'); +const BridgeArbitrumL2Mock = artifacts.require('BridgeArbitrumL2Mock'); +const BridgeOptimismMock = artifacts.require('BridgeOptimismMock'); +const BridgePolygonChildMock = artifacts.require('BridgePolygonChildMock'); + +class BridgeHelper { + static async deploy (type) { + return new BridgeHelper(await deployBridge(type)); + } + + constructor (bridge) { + this.bridge = bridge; + this.address = bridge.address; + } + + call (from, target, selector = undefined, args = []) { + return this.bridge.relayAs( + target.address || target, + selector + ? target.contract.methods[selector](...args).encodeABI() + : '0x', + from, + ); + } +} + +async function deployBridge (type = 'Arbitrum-L2') { + switch (type) { + case 'AMB': + return BridgeAMBMock.new(); + + case 'Arbitrum-L1': + return BridgeArbitrumL1Mock.new(); + + case 'Arbitrum-L2': { + const instance = await BridgeArbitrumL2Mock.new(); + const code = await web3.eth.getCode(instance.address); + await promisify(web3.currentProvider.send.bind(web3.currentProvider))({ + jsonrpc: '2.0', + method: 'hardhat_setCode', + params: [ '0x0000000000000000000000000000000000000064', code ], + id: new Date().getTime(), + }); + return BridgeArbitrumL2Mock.at('0x0000000000000000000000000000000000000064'); + } + + case 'Optimism': + return BridgeOptimismMock.new(); + + case 'Polygon-Child': + return BridgePolygonChildMock.new(); + + default: + throw new Error(`CrossChain: ${type} is not supported`); + } +} + +module.exports = { + BridgeHelper, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/customError.js b/lib/openzeppelin-contracts/test/helpers/customError.js new file mode 100644 index 0000000..8fea434 --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/customError.js @@ -0,0 +1,24 @@ +const { config } = require('hardhat'); + +const optimizationsEnabled = config.solidity.compilers.some(c => c.settings.optimizer.enabled); + +/** Revert handler that supports custom errors. */ +async function expectRevertCustomError (promise, reason) { + try { + await promise; + expect.fail('Expected promise to throw but it didn\'t'); + } catch (revert) { + if (reason) { + if (optimizationsEnabled) { + // Optimizations currently mess with Hardhat's decoding of custom errors + expect(revert.message).to.include.oneOf([reason, 'unrecognized return data or custom error']); + } else { + expect(revert.message).to.include(reason); + } + } + } +}; + +module.exports = { + expectRevertCustomError, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/eip712.js b/lib/openzeppelin-contracts/test/helpers/eip712.js new file mode 100644 index 0000000..8a64b8a --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/eip712.js @@ -0,0 +1,30 @@ +const ethSigUtil = require('eth-sig-util'); + +const EIP712Domain = [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, +]; + +const Permit = [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, +]; + +async function domainSeparator (name, version, chainId, verifyingContract) { + return '0x' + ethSigUtil.TypedDataUtils.hashStruct( + 'EIP712Domain', + { name, version, chainId, verifyingContract }, + { EIP712Domain }, + ).toString('hex'); +} + +module.exports = { + EIP712Domain, + Permit, + domainSeparator, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/enums.js b/lib/openzeppelin-contracts/test/helpers/enums.js new file mode 100644 index 0000000..26825de --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/enums.js @@ -0,0 +1,29 @@ +const { BN } = require('@openzeppelin/test-helpers'); + +function Enum (...options) { + return Object.fromEntries(options.map((key, i) => [ key, new BN(i) ])); +} + +module.exports = { + Enum, + ProposalState: Enum( + 'Pending', + 'Active', + 'Canceled', + 'Defeated', + 'Succeeded', + 'Queued', + 'Expired', + 'Executed', + ), + VoteType: Enum( + 'Against', + 'For', + 'Abstain', + ), + Rounding: Enum( + 'Down', + 'Up', + 'Zero', + ), +}; diff --git a/lib/openzeppelin-contracts/test/helpers/erc1967.js b/lib/openzeppelin-contracts/test/helpers/erc1967.js new file mode 100644 index 0000000..aab0e22 --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/erc1967.js @@ -0,0 +1,24 @@ +const ImplementationLabel = 'eip1967.proxy.implementation'; +const AdminLabel = 'eip1967.proxy.admin'; +const BeaconLabel = 'eip1967.proxy.beacon'; + +function labelToSlot (label) { + return '0x' + web3.utils.toBN(web3.utils.keccak256(label)).subn(1).toString(16); +} + +function getSlot (address, slot) { + return web3.eth.getStorageAt( + web3.utils.isAddress(address) ? address : address.address, + web3.utils.isHex(slot) ? slot : labelToSlot(slot), + ); +} + +module.exports = { + ImplementationLabel, + AdminLabel, + BeaconLabel, + ImplementationSlot: labelToSlot(ImplementationLabel), + AdminSlot: labelToSlot(AdminLabel), + BeaconSlot: labelToSlot(BeaconLabel), + getSlot, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/governance.js b/lib/openzeppelin-contracts/test/helpers/governance.js new file mode 100644 index 0000000..c56ec4c --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/governance.js @@ -0,0 +1,211 @@ +const { time } = require('@openzeppelin/test-helpers'); + +function zip (...args) { + return Array(Math.max(...args.map(array => array.length))) + .fill() + .map((_, i) => args.map(array => array[i])); +} + +function concatHex (...args) { + return web3.utils.bytesToHex([].concat(...args.map(h => web3.utils.hexToBytes(h || '0x')))); +} + +function concatOpts (args, opts = null) { + return opts ? args.concat(opts) : args; +} + +class GovernorHelper { + constructor (governor) { + this.governor = governor; + } + + delegate (delegation = {}, opts = null) { + return Promise.all([ + delegation.token.delegate(delegation.to, { from: delegation.to }), + delegation.value && + delegation.token.transfer(...concatOpts([ delegation.to, delegation.value ]), opts), + delegation.tokenId && + delegation.token.ownerOf(delegation.tokenId).then(owner => + delegation.token.transferFrom(...concatOpts([ owner, delegation.to, delegation.tokenId ], opts)), + ), + ]); + } + + propose (opts = null) { + const proposal = this.currentProposal; + + return this.governor.methods[ + proposal.useCompatibilityInterface + ? 'propose(address[],uint256[],string[],bytes[],string)' + : 'propose(address[],uint256[],bytes[],string)' + ](...concatOpts(proposal.fullProposal, opts)); + } + + queue (opts = null) { + const proposal = this.currentProposal; + + return proposal.useCompatibilityInterface + ? this.governor.methods['queue(uint256)'](...concatOpts( + [ proposal.id ], + opts, + )) + : this.governor.methods['queue(address[],uint256[],bytes[],bytes32)'](...concatOpts( + proposal.shortProposal, + opts, + )); + } + + execute (opts = null) { + const proposal = this.currentProposal; + + return proposal.useCompatibilityInterface + ? this.governor.methods['execute(uint256)'](...concatOpts( + [ proposal.id ], + opts, + )) + : this.governor.methods['execute(address[],uint256[],bytes[],bytes32)'](...concatOpts( + proposal.shortProposal, + opts, + )); + } + + cancel (opts = null) { + const proposal = this.currentProposal; + + return proposal.useCompatibilityInterface + ? this.governor.methods['cancel(uint256)'](...concatOpts( + [ proposal.id ], + opts, + )) + : this.governor.methods['cancel(address[],uint256[],bytes[],bytes32)'](...concatOpts( + proposal.shortProposal, + opts, + )); + } + + vote (vote = {}, opts = null) { + const proposal = this.currentProposal; + + return vote.signature + // if signature, and either params or reason → + ? vote.params || vote.reason + ? vote.signature({ + proposalId: proposal.id, + support: vote.support, + reason: vote.reason || '', + params: vote.params || '', + }).then(({ v, r, s }) => this.governor.castVoteWithReasonAndParamsBySig(...concatOpts( + [ proposal.id, vote.support, vote.reason || '', vote.params || '', v, r, s ], + opts, + ))) + : vote.signature({ + proposalId: proposal.id, + support: vote.support, + }).then(({ v, r, s }) => this.governor.castVoteBySig(...concatOpts( + [ proposal.id, vote.support, v, r, s ], + opts, + ))) + : vote.params + // otherwise if params + ? this.governor.castVoteWithReasonAndParams(...concatOpts( + [ proposal.id, vote.support, vote.reason || '', vote.params ], + opts, + )) + : vote.reason + // otherwise if reason + ? this.governor.castVoteWithReason(...concatOpts( + [ proposal.id, vote.support, vote.reason ], + opts, + )) + : this.governor.castVote(...concatOpts( + [ proposal.id, vote.support ], + opts, + )); + } + + waitForSnapshot (offset = 0) { + const proposal = this.currentProposal; + return this.governor.proposalSnapshot(proposal.id) + .then(blockNumber => time.advanceBlockTo(blockNumber.addn(offset))); + } + + waitForDeadline (offset = 0) { + const proposal = this.currentProposal; + return this.governor.proposalDeadline(proposal.id) + .then(blockNumber => time.advanceBlockTo(blockNumber.addn(offset))); + } + + waitForEta (offset = 0) { + const proposal = this.currentProposal; + return this.governor.proposalEta(proposal.id) + .then(timestamp => time.increaseTo(timestamp.addn(offset))); + } + + /** + * Specify a proposal either as + * 1) an array of objects [{ target, value, data, signature? }] + * 2) an object of arrays { targets: [], values: [], data: [], signatures?: [] } + */ + setProposal (actions, description) { + let targets, values, signatures, data, useCompatibilityInterface; + + if (Array.isArray(actions)) { + useCompatibilityInterface = actions.some(a => 'signature' in a); + targets = actions.map(a => a.target); + values = actions.map(a => a.value || '0'); + signatures = actions.map(a => a.signature || ''); + data = actions.map(a => a.data || '0x'); + } else { + useCompatibilityInterface = Array.isArray(actions.signatures); + ({ targets, values, signatures = [], data } = actions); + } + + const fulldata = zip(signatures.map(s => s && web3.eth.abi.encodeFunctionSignature(s)), data) + .map(hexs => concatHex(...hexs)); + + const descriptionHash = web3.utils.keccak256(description); + + // condensed version for queueing end executing + const shortProposal = [ + targets, + values, + fulldata, + descriptionHash, + ]; + + // full version for proposing + const fullProposal = [ + targets, + values, + ...(useCompatibilityInterface ? [ signatures ] : []), + data, + description, + ]; + + // proposal id + const id = web3.utils.toBN(web3.utils.keccak256(web3.eth.abi.encodeParameters( + [ 'address[]', 'uint256[]', 'bytes[]', 'bytes32' ], + shortProposal, + ))); + + this.currentProposal = { + id, + targets, + values, + signatures, + data, + fulldata, + description, + descriptionHash, + shortProposal, + fullProposal, + useCompatibilityInterface, + }; + + return this.currentProposal; + } +} + +module.exports = { + GovernorHelper, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/sign.js b/lib/openzeppelin-contracts/test/helpers/sign.js new file mode 100644 index 0000000..4c14d1f --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/sign.js @@ -0,0 +1,47 @@ +function toEthSignedMessageHash (messageHex) { + const messageBuffer = Buffer.from(messageHex.substring(2), 'hex'); + const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`); + return web3.utils.sha3(Buffer.concat([prefix, messageBuffer])); +} + +/** + * Create a signer between a contract and a signer for a voucher of method, args, and redeemer + * Note that `method` is the web3 method, not the truffle-contract method + * @param contract TruffleContract + * @param signer address + * @param redeemer address + * @param methodName string + * @param methodArgs any[] + */ +const getSignFor = (contract, signer) => (redeemer, methodName, methodArgs = []) => { + const parts = [ + contract.address, + redeemer, + ]; + + const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string length + const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length + const DUMMY_SIGNATURE = `0x${web3.utils.padLeft('', REAL_SIGNATURE_SIZE)}`; + + // if we have a method, add it to the parts that we're signing + if (methodName) { + if (methodArgs.length > 0) { + parts.push( + contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE])).encodeABI() + .slice(0, -1 * PADDED_SIGNATURE_SIZE), + ); + } else { + const abi = contract.abi.find(abi => abi.name === methodName); + parts.push(abi.signature); + } + } + + // return the signature of the "Ethereum Signed Message" hash of the hash of `parts` + const messageHex = web3.utils.soliditySha3(...parts); + return web3.eth.sign(messageHex, signer); +}; + +module.exports = { + toEthSignedMessageHash, + getSignFor, +}; diff --git a/lib/openzeppelin-contracts/test/helpers/txpool.js b/lib/openzeppelin-contracts/test/helpers/txpool.js new file mode 100644 index 0000000..895246b --- /dev/null +++ b/lib/openzeppelin-contracts/test/helpers/txpool.js @@ -0,0 +1,40 @@ +const { network } = require('hardhat'); +const { promisify } = require('util'); + +const queue = promisify(setImmediate); + +async function countPendingTransactions () { + return parseInt( + await network.provider.send('eth_getBlockTransactionCountByNumber', ['pending']), + ); +} + +async function batchInBlock (txs) { + try { + // disable auto-mining + await network.provider.send('evm_setAutomine', [false]); + // send all transactions + const promises = txs.map(fn => fn()); + // wait for node to have all pending transactions + while (txs.length > await countPendingTransactions()) { + await queue(); + } + // mine one block + await network.provider.send('evm_mine'); + // fetch receipts + const receipts = await Promise.all(promises); + // Sanity check, all tx should be in the same block + const minedBlocks = new Set(receipts.map(({ receipt }) => receipt.blockNumber)); + expect(minedBlocks.size).to.equal(1); + + return receipts; + } finally { + // enable auto-mining + await network.provider.send('evm_setAutomine', [true]); + } +} + +module.exports = { + countPendingTransactions, + batchInBlock, +}; diff --git a/lib/openzeppelin-contracts/test/metatx/ERC2771Context.test.js b/lib/openzeppelin-contracts/test/metatx/ERC2771Context.test.js new file mode 100644 index 0000000..8db92ab --- /dev/null +++ b/lib/openzeppelin-contracts/test/metatx/ERC2771Context.test.js @@ -0,0 +1,109 @@ +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; +const { EIP712Domain } = require('../helpers/eip712'); + +const { expectEvent } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const ERC2771ContextMock = artifacts.require('ERC2771ContextMock'); +const MinimalForwarder = artifacts.require('MinimalForwarder'); +const ContextMockCaller = artifacts.require('ContextMockCaller'); + +const { shouldBehaveLikeRegularContext } = require('../utils/Context.behavior'); + +const name = 'MinimalForwarder'; +const version = '0.0.1'; + +contract('ERC2771Context', function (accounts) { + beforeEach(async function () { + this.forwarder = await MinimalForwarder.new(); + this.recipient = await ERC2771ContextMock.new(this.forwarder.address); + + this.domain = { + name, + version, + chainId: await web3.eth.getChainId(), + verifyingContract: this.forwarder.address, + }; + this.types = { + EIP712Domain, + ForwardRequest: [ + { name: 'from', type: 'address' }, + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'gas', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + ], + }; + }); + + it('recognize trusted forwarder', async function () { + expect(await this.recipient.isTrustedForwarder(this.forwarder.address)); + }); + + context('when called directly', function () { + beforeEach(async function () { + this.context = this.recipient; // The Context behavior expects the contract in this.context + this.caller = await ContextMockCaller.new(); + }); + + shouldBehaveLikeRegularContext(...accounts); + }); + + context('when receiving a relayed call', function () { + beforeEach(async function () { + this.wallet = Wallet.generate(); + this.sender = web3.utils.toChecksumAddress(this.wallet.getAddressString()); + this.data = { + types: this.types, + domain: this.domain, + primaryType: 'ForwardRequest', + }; + }); + + describe('msgSender', function () { + it('returns the relayed transaction original sender', async function () { + const data = this.recipient.contract.methods.msgSender().encodeABI(); + + const req = { + from: this.sender, + to: this.recipient.address, + value: '0', + gas: '100000', + nonce: (await this.forwarder.getNonce(this.sender)).toString(), + data, + }; + + const sign = ethSigUtil.signTypedMessage(this.wallet.getPrivateKey(), { data: { ...this.data, message: req } }); + expect(await this.forwarder.verify(req, sign)).to.equal(true); + + const { tx } = await this.forwarder.execute(req, sign); + await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Sender', { sender: this.sender }); + }); + }); + + describe('msgData', function () { + it('returns the relayed transaction original data', async function () { + const integerValue = '42'; + const stringValue = 'OpenZeppelin'; + const data = this.recipient.contract.methods.msgData(integerValue, stringValue).encodeABI(); + + const req = { + from: this.sender, + to: this.recipient.address, + value: '0', + gas: '100000', + nonce: (await this.forwarder.getNonce(this.sender)).toString(), + data, + }; + + const sign = ethSigUtil.signTypedMessage(this.wallet.getPrivateKey(), { data: { ...this.data, message: req } }); + expect(await this.forwarder.verify(req, sign)).to.equal(true); + + const { tx } = await this.forwarder.execute(req, sign); + await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Data', { data, integerValue, stringValue }); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/metatx/MinimalForwarder.test.js b/lib/openzeppelin-contracts/test/metatx/MinimalForwarder.test.js new file mode 100644 index 0000000..b8984e4 --- /dev/null +++ b/lib/openzeppelin-contracts/test/metatx/MinimalForwarder.test.js @@ -0,0 +1,184 @@ +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; +const { EIP712Domain } = require('../helpers/eip712'); + +const { expectRevert, constants } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const MinimalForwarder = artifacts.require('MinimalForwarder'); +const CallReceiverMock = artifacts.require('CallReceiverMock'); + +const name = 'MinimalForwarder'; +const version = '0.0.1'; + +contract('MinimalForwarder', function (accounts) { + beforeEach(async function () { + this.forwarder = await MinimalForwarder.new(); + this.domain = { + name, + version, + chainId: await web3.eth.getChainId(), + verifyingContract: this.forwarder.address, + }; + this.types = { + EIP712Domain, + ForwardRequest: [ + { name: 'from', type: 'address' }, + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'gas', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + ], + }; + }); + + context('with message', function () { + beforeEach(async function () { + this.wallet = Wallet.generate(); + this.sender = web3.utils.toChecksumAddress(this.wallet.getAddressString()); + this.req = { + from: this.sender, + to: constants.ZERO_ADDRESS, + value: '0', + gas: '100000', + nonce: Number(await this.forwarder.getNonce(this.sender)), + data: '0x', + }; + this.sign = () => ethSigUtil.signTypedMessage( + this.wallet.getPrivateKey(), + { + data: { + types: this.types, + domain: this.domain, + primaryType: 'ForwardRequest', + message: this.req, + }, + }, + ); + }); + + context('verify', function () { + context('valid signature', function () { + beforeEach(async function () { + expect(await this.forwarder.getNonce(this.req.from)) + .to.be.bignumber.equal(web3.utils.toBN(this.req.nonce)); + }); + + it('success', async function () { + expect(await this.forwarder.verify(this.req, this.sign())).to.be.equal(true); + }); + + afterEach(async function () { + expect(await this.forwarder.getNonce(this.req.from)) + .to.be.bignumber.equal(web3.utils.toBN(this.req.nonce)); + }); + }); + + context('invalid signature', function () { + it('tampered from', async function () { + expect(await this.forwarder.verify({ ...this.req, from: accounts[0] }, this.sign())) + .to.be.equal(false); + }); + it('tampered to', async function () { + expect(await this.forwarder.verify({ ...this.req, to: accounts[0] }, this.sign())) + .to.be.equal(false); + }); + it('tampered value', async function () { + expect(await this.forwarder.verify({ ...this.req, value: web3.utils.toWei('1') }, this.sign())) + .to.be.equal(false); + }); + it('tampered nonce', async function () { + expect(await this.forwarder.verify({ ...this.req, nonce: this.req.nonce + 1 }, this.sign())) + .to.be.equal(false); + }); + it('tampered data', async function () { + expect(await this.forwarder.verify({ ...this.req, data: '0x1742' }, this.sign())) + .to.be.equal(false); + }); + it('tampered signature', async function () { + const tamperedsign = web3.utils.hexToBytes(this.sign()); + tamperedsign[42] ^= 0xff; + expect(await this.forwarder.verify(this.req, web3.utils.bytesToHex(tamperedsign))) + .to.be.equal(false); + }); + }); + }); + + context('execute', function () { + context('valid signature', function () { + beforeEach(async function () { + expect(await this.forwarder.getNonce(this.req.from)) + .to.be.bignumber.equal(web3.utils.toBN(this.req.nonce)); + }); + + it('success', async function () { + await this.forwarder.execute(this.req, this.sign()); // expect to not revert + }); + + afterEach(async function () { + expect(await this.forwarder.getNonce(this.req.from)) + .to.be.bignumber.equal(web3.utils.toBN(this.req.nonce + 1)); + }); + }); + + context('invalid signature', function () { + it('tampered from', async function () { + await expectRevert( + this.forwarder.execute({ ...this.req, from: accounts[0] }, this.sign()), + 'MinimalForwarder: signature does not match request', + ); + }); + it('tampered to', async function () { + await expectRevert( + this.forwarder.execute({ ...this.req, to: accounts[0] }, this.sign()), + 'MinimalForwarder: signature does not match request', + ); + }); + it('tampered value', async function () { + await expectRevert( + this.forwarder.execute({ ...this.req, value: web3.utils.toWei('1') }, this.sign()), + 'MinimalForwarder: signature does not match request', + ); + }); + it('tampered nonce', async function () { + await expectRevert( + this.forwarder.execute({ ...this.req, nonce: this.req.nonce + 1 }, this.sign()), + 'MinimalForwarder: signature does not match request', + ); + }); + it('tampered data', async function () { + await expectRevert( + this.forwarder.execute({ ...this.req, data: '0x1742' }, this.sign()), + 'MinimalForwarder: signature does not match request', + ); + }); + it('tampered signature', async function () { + const tamperedsign = web3.utils.hexToBytes(this.sign()); + tamperedsign[42] ^= 0xff; + await expectRevert( + this.forwarder.execute(this.req, web3.utils.bytesToHex(tamperedsign)), + 'MinimalForwarder: signature does not match request', + ); + }); + }); + + it('bubble out of gas', async function () { + const receiver = await CallReceiverMock.new(); + const gasAvailable = 100000; + this.req.to = receiver.address; + this.req.data = receiver.contract.methods.mockFunctionOutOfGas().encodeABI(); + this.req.gas = 1000000; + + await expectRevert.assertion( + this.forwarder.execute(this.req, this.sign(), { gas: gasAvailable }), + ); + + const { transactions } = await web3.eth.getBlock('latest'); + const { gasUsed } = await web3.eth.getTransactionReceipt(transactions[0]); + + expect(gasUsed).to.be.equal(gasAvailable); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/migrate-imports.test.js b/lib/openzeppelin-contracts/test/migrate-imports.test.js new file mode 100644 index 0000000..639767c --- /dev/null +++ b/lib/openzeppelin-contracts/test/migrate-imports.test.js @@ -0,0 +1,29 @@ +const path = require('path'); +const { promises: fs, constants: { F_OK } } = require('fs'); +const { expect } = require('chai'); + +const { pathUpdates, updateImportPaths, getUpgradeablePath } = require('../scripts/migrate-imports.js'); + +describe('migrate-imports.js', function () { + it('every new path exists', async function () { + for (const p of Object.values(pathUpdates)) { + try { + await fs.access(path.join('contracts', p), F_OK); + } catch (e) { + await fs.access(path.join('contracts', getUpgradeablePath(p)), F_OK); + } + } + }); + + it('replaces import paths in a file', async function () { + const source = ` +import '@openzeppelin/contracts/math/Math.sol'; +import '@openzeppelin/contracts-upgradeable/math/MathUpgradeable.sol'; + `; + const expected = ` +import '@openzeppelin/contracts/utils/math/Math.sol'; +import '@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol'; + `; + expect(updateImportPaths(source)).to.equal(expected); + }); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/Clones.behaviour.js b/lib/openzeppelin-contracts/test/proxy/Clones.behaviour.js new file mode 100644 index 0000000..81c5ee3 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/Clones.behaviour.js @@ -0,0 +1,150 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const DummyImplementation = artifacts.require('DummyImplementation'); + +module.exports = function shouldBehaveLikeClone (createClone) { + before('deploy implementation', async function () { + this.implementation = web3.utils.toChecksumAddress((await DummyImplementation.new()).address); + }); + + const assertProxyInitialization = function ({ value, balance }) { + it('initializes the proxy', async function () { + const dummy = new DummyImplementation(this.proxy); + expect(await dummy.value()).to.be.bignumber.equal(value.toString()); + }); + + it('has expected balance', async function () { + expect(await web3.eth.getBalance(this.proxy)).to.be.bignumber.equal(balance.toString()); + }); + }; + + describe('initialization without parameters', function () { + describe('non payable', function () { + const expectedInitializedValue = 10; + const initializeData = new DummyImplementation('').contract.methods['initializeNonPayable()']().encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createClone(this.implementation, initializeData) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + it('reverts', async function () { + await expectRevert.unspecified( + createClone(this.implementation, initializeData, { value }), + ); + }); + }); + }); + + describe('payable', function () { + const expectedInitializedValue = 100; + const initializeData = new DummyImplementation('').contract.methods['initializePayable()']().encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createClone(this.implementation, initializeData) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + beforeEach('creating proxy', async function () { + this.proxy = ( + await createClone(this.implementation, initializeData, { value }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: value, + }); + }); + }); + }); + + describe('initialization with parameters', function () { + describe('non payable', function () { + const expectedInitializedValue = 10; + const initializeData = new DummyImplementation('').contract + .methods.initializeNonPayableWithValue(expectedInitializedValue).encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createClone(this.implementation, initializeData) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + it('reverts', async function () { + await expectRevert.unspecified( + createClone(this.implementation, initializeData, { value }), + ); + }); + }); + }); + + describe('payable', function () { + const expectedInitializedValue = 42; + const initializeData = new DummyImplementation('').contract + .methods.initializePayableWithValue(expectedInitializedValue).encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createClone(this.implementation, initializeData) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + beforeEach('creating proxy', async function () { + this.proxy = ( + await createClone(this.implementation, initializeData, { value }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: value, + }); + }); + }); + }); +}; diff --git a/lib/openzeppelin-contracts/test/proxy/Clones.test.js b/lib/openzeppelin-contracts/test/proxy/Clones.test.js new file mode 100644 index 0000000..65e5ac1 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/Clones.test.js @@ -0,0 +1,69 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { computeCreate2Address } = require('../helpers/create2'); +const { expect } = require('chai'); + +const shouldBehaveLikeClone = require('./Clones.behaviour'); + +const ClonesMock = artifacts.require('ClonesMock'); + +contract('Clones', function (accounts) { + describe('clone', function () { + shouldBehaveLikeClone(async (implementation, initData, opts = {}) => { + const factory = await ClonesMock.new(); + const receipt = await factory.clone(implementation, initData, { value: opts.value }); + const address = receipt.logs.find(({ event }) => event === 'NewInstance').args.instance; + return { address }; + }); + }); + + describe('cloneDeterministic', function () { + shouldBehaveLikeClone(async (implementation, initData, opts = {}) => { + const salt = web3.utils.randomHex(32); + const factory = await ClonesMock.new(); + const receipt = await factory.cloneDeterministic(implementation, salt, initData, { value: opts.value }); + const address = receipt.logs.find(({ event }) => event === 'NewInstance').args.instance; + return { address }; + }); + + it('address already used', async function () { + const implementation = web3.utils.randomHex(20); + const salt = web3.utils.randomHex(32); + const factory = await ClonesMock.new(); + // deploy once + expectEvent( + await factory.cloneDeterministic(implementation, salt, '0x'), + 'NewInstance', + ); + // deploy twice + await expectRevert( + factory.cloneDeterministic(implementation, salt, '0x'), + 'ERC1167: create2 failed', + ); + }); + + it('address prediction', async function () { + const implementation = web3.utils.randomHex(20); + const salt = web3.utils.randomHex(32); + const factory = await ClonesMock.new(); + const predicted = await factory.predictDeterministicAddress(implementation, salt); + + const creationCode = [ + '0x3d602d80600a3d3981f3363d3d373d3d3d363d73', + implementation.replace(/0x/, '').toLowerCase(), + '5af43d82803e903d91602b57fd5bf3', + ].join(''); + + expect(computeCreate2Address( + salt, + creationCode, + factory.address, + )).to.be.equal(predicted); + + expectEvent( + await factory.cloneDeterministic(implementation, salt, '0x'), + 'NewInstance', + { instance: predicted }, + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/ERC1967/ERC1967Proxy.test.js b/lib/openzeppelin-contracts/test/proxy/ERC1967/ERC1967Proxy.test.js new file mode 100644 index 0000000..22df960 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/ERC1967/ERC1967Proxy.test.js @@ -0,0 +1,13 @@ +const shouldBehaveLikeProxy = require('../Proxy.behaviour'); + +const ERC1967Proxy = artifacts.require('ERC1967Proxy'); + +contract('ERC1967Proxy', function (accounts) { + const [proxyAdminOwner] = accounts; + + const createProxy = async function (implementation, _admin, initData, opts) { + return ERC1967Proxy.new(implementation, initData, opts); + }; + + shouldBehaveLikeProxy(createProxy, undefined, proxyAdminOwner); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/Proxy.behaviour.js b/lib/openzeppelin-contracts/test/proxy/Proxy.behaviour.js new file mode 100644 index 0000000..435792f --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/Proxy.behaviour.js @@ -0,0 +1,224 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); +const { getSlot, ImplementationSlot } = require('../helpers/erc1967'); + +const { expect } = require('chai'); + +const DummyImplementation = artifacts.require('DummyImplementation'); + +module.exports = function shouldBehaveLikeProxy (createProxy, proxyAdminAddress, proxyCreator) { + it('cannot be initialized with a non-contract address', async function () { + const nonContractAddress = proxyCreator; + const initializeData = Buffer.from(''); + await expectRevert.unspecified( + createProxy(nonContractAddress, proxyAdminAddress, initializeData, { + from: proxyCreator, + }), + ); + }); + + before('deploy implementation', async function () { + this.implementation = web3.utils.toChecksumAddress((await DummyImplementation.new()).address); + }); + + const assertProxyInitialization = function ({ value, balance }) { + it('sets the implementation address', async function () { + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.equal(this.implementation); + }); + + it('initializes the proxy', async function () { + const dummy = new DummyImplementation(this.proxy); + expect(await dummy.value()).to.be.bignumber.equal(value.toString()); + }); + + it('has expected balance', async function () { + expect(await web3.eth.getBalance(this.proxy)).to.be.bignumber.equal(balance.toString()); + }); + }; + + describe('without initialization', function () { + const initializeData = Buffer.from(''); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + }) + ).address; + }); + + assertProxyInitialization({ value: 0, balance: 0 }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + value, + }) + ).address; + }); + + assertProxyInitialization({ value: 0, balance: value }); + }); + }); + + describe('initialization without parameters', function () { + describe('non payable', function () { + const expectedInitializedValue = 10; + const initializeData = new DummyImplementation('').contract.methods['initializeNonPayable()']().encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + it('reverts', async function () { + await expectRevert.unspecified( + createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator, value }), + ); + }); + }); + }); + + describe('payable', function () { + const expectedInitializedValue = 100; + const initializeData = new DummyImplementation('').contract.methods['initializePayable()']().encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + value, + }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: value, + }); + }); + }); + }); + + describe('initialization with parameters', function () { + describe('non payable', function () { + const expectedInitializedValue = 10; + const initializeData = new DummyImplementation('').contract + .methods.initializeNonPayableWithValue(expectedInitializedValue).encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + it('reverts', async function () { + await expectRevert.unspecified( + createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator, value }), + ); + }); + }); + }); + + describe('payable', function () { + const expectedInitializedValue = 42; + const initializeData = new DummyImplementation('').contract + .methods.initializePayableWithValue(expectedInitializedValue).encodeABI(); + + describe('when not sending balance', function () { + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: 0, + }); + }); + + describe('when sending some balance', function () { + const value = 10e5; + + beforeEach('creating proxy', async function () { + this.proxy = ( + await createProxy(this.implementation, proxyAdminAddress, initializeData, { + from: proxyCreator, + value, + }) + ).address; + }); + + assertProxyInitialization({ + value: expectedInitializedValue, + balance: value, + }); + }); + }); + + describe('reverting initialization', function () { + const initializeData = new DummyImplementation('').contract + .methods.reverts().encodeABI(); + + it('reverts', async function () { + await expectRevert( + createProxy(this.implementation, proxyAdminAddress, initializeData, { from: proxyCreator }), + 'DummyImplementation reverted', + ); + }); + }); + }); +}; diff --git a/lib/openzeppelin-contracts/test/proxy/beacon/BeaconProxy.test.js b/lib/openzeppelin-contracts/test/proxy/beacon/BeaconProxy.test.js new file mode 100644 index 0000000..0a4a8d0 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/beacon/BeaconProxy.test.js @@ -0,0 +1,156 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); +const { getSlot, BeaconSlot } = require('../../helpers/erc1967'); + +const { expect } = require('chai'); + +const UpgradeableBeacon = artifacts.require('UpgradeableBeacon'); +const BeaconProxy = artifacts.require('BeaconProxy'); +const DummyImplementation = artifacts.require('DummyImplementation'); +const DummyImplementationV2 = artifacts.require('DummyImplementationV2'); +const BadBeaconNoImpl = artifacts.require('BadBeaconNoImpl'); +const BadBeaconNotContract = artifacts.require('BadBeaconNotContract'); + +contract('BeaconProxy', function (accounts) { + const [anotherAccount] = accounts; + + describe('bad beacon is not accepted', async function () { + it('non-contract beacon', async function () { + await expectRevert( + BeaconProxy.new(anotherAccount, '0x'), + 'ERC1967: new beacon is not a contract', + ); + }); + + it('non-compliant beacon', async function () { + const beacon = await BadBeaconNoImpl.new(); + await expectRevert.unspecified( + BeaconProxy.new(beacon.address, '0x'), + ); + }); + + it('non-contract implementation', async function () { + const beacon = await BadBeaconNotContract.new(); + await expectRevert( + BeaconProxy.new(beacon.address, '0x'), + 'ERC1967: beacon implementation is not a contract', + ); + }); + }); + + before('deploy implementation', async function () { + this.implementationV0 = await DummyImplementation.new(); + this.implementationV1 = await DummyImplementationV2.new(); + }); + + describe('initialization', function () { + before(function () { + this.assertInitialized = async ({ value, balance }) => { + const beaconSlot = await getSlot(this.proxy, BeaconSlot); + const beaconAddress = web3.utils.toChecksumAddress(beaconSlot.substr(-40)); + expect(beaconAddress).to.equal(this.beacon.address); + + const dummy = new DummyImplementation(this.proxy.address); + expect(await dummy.value()).to.bignumber.eq(value); + + expect(await web3.eth.getBalance(this.proxy.address)).to.bignumber.eq(balance); + }; + }); + + beforeEach('deploy beacon', async function () { + this.beacon = await UpgradeableBeacon.new(this.implementationV0.address); + }); + + it('no initialization', async function () { + const data = Buffer.from(''); + const balance = '10'; + this.proxy = await BeaconProxy.new(this.beacon.address, data, { value: balance }); + await this.assertInitialized({ value: '0', balance }); + }); + + it('non-payable initialization', async function () { + const value = '55'; + const data = this.implementationV0.contract.methods + .initializeNonPayableWithValue(value) + .encodeABI(); + this.proxy = await BeaconProxy.new(this.beacon.address, data); + await this.assertInitialized({ value, balance: '0' }); + }); + + it('payable initialization', async function () { + const value = '55'; + const data = this.implementationV0.contract.methods + .initializePayableWithValue(value) + .encodeABI(); + const balance = '100'; + this.proxy = await BeaconProxy.new(this.beacon.address, data, { value: balance }); + await this.assertInitialized({ value, balance }); + }); + + it('reverting initialization', async function () { + const data = this.implementationV0.contract.methods.reverts().encodeABI(); + await expectRevert( + BeaconProxy.new(this.beacon.address, data), + 'DummyImplementation reverted', + ); + }); + }); + + it('upgrade a proxy by upgrading its beacon', async function () { + const beacon = await UpgradeableBeacon.new(this.implementationV0.address); + + const value = '10'; + const data = this.implementationV0.contract.methods + .initializeNonPayableWithValue(value) + .encodeABI(); + const proxy = await BeaconProxy.new(beacon.address, data); + + const dummy = new DummyImplementation(proxy.address); + + // test initial values + expect(await dummy.value()).to.bignumber.eq(value); + + // test initial version + expect(await dummy.version()).to.eq('V1'); + + // upgrade beacon + await beacon.upgradeTo(this.implementationV1.address); + + // test upgraded version + expect(await dummy.version()).to.eq('V2'); + }); + + it('upgrade 2 proxies by upgrading shared beacon', async function () { + const value1 = '10'; + const value2 = '42'; + + const beacon = await UpgradeableBeacon.new(this.implementationV0.address); + + const proxy1InitializeData = this.implementationV0.contract.methods + .initializeNonPayableWithValue(value1) + .encodeABI(); + const proxy1 = await BeaconProxy.new(beacon.address, proxy1InitializeData); + + const proxy2InitializeData = this.implementationV0.contract.methods + .initializeNonPayableWithValue(value2) + .encodeABI(); + const proxy2 = await BeaconProxy.new(beacon.address, proxy2InitializeData); + + const dummy1 = new DummyImplementation(proxy1.address); + const dummy2 = new DummyImplementation(proxy2.address); + + // test initial values + expect(await dummy1.value()).to.bignumber.eq(value1); + expect(await dummy2.value()).to.bignumber.eq(value2); + + // test initial version + expect(await dummy1.version()).to.eq('V1'); + expect(await dummy2.version()).to.eq('V1'); + + // upgrade beacon + await beacon.upgradeTo(this.implementationV1.address); + + // test upgraded version + expect(await dummy1.version()).to.eq('V2'); + expect(await dummy2.version()).to.eq('V2'); + }); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/beacon/UpgradeableBeacon.test.js b/lib/openzeppelin-contracts/test/proxy/beacon/UpgradeableBeacon.test.js new file mode 100644 index 0000000..0c49906 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/beacon/UpgradeableBeacon.test.js @@ -0,0 +1,50 @@ +const { expectRevert, expectEvent } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const UpgradeableBeacon = artifacts.require('UpgradeableBeacon'); +const Implementation1 = artifacts.require('Implementation1'); +const Implementation2 = artifacts.require('Implementation2'); + +contract('UpgradeableBeacon', function (accounts) { + const [owner, other] = accounts; + + it('cannot be created with non-contract implementation', async function () { + await expectRevert( + UpgradeableBeacon.new(accounts[0]), + 'UpgradeableBeacon: implementation is not a contract', + ); + }); + + context('once deployed', async function () { + beforeEach('deploying beacon', async function () { + this.v1 = await Implementation1.new(); + this.beacon = await UpgradeableBeacon.new(this.v1.address, { from: owner }); + }); + + it('returns implementation', async function () { + expect(await this.beacon.implementation()).to.equal(this.v1.address); + }); + + it('can be upgraded by the owner', async function () { + const v2 = await Implementation2.new(); + const receipt = await this.beacon.upgradeTo(v2.address, { from: owner }); + expectEvent(receipt, 'Upgraded', { implementation: v2.address }); + expect(await this.beacon.implementation()).to.equal(v2.address); + }); + + it('cannot be upgraded to a non-contract', async function () { + await expectRevert( + this.beacon.upgradeTo(other, { from: owner }), + 'UpgradeableBeacon: implementation is not a contract', + ); + }); + + it('cannot be upgraded by other account', async function () { + const v2 = await Implementation2.new(); + await expectRevert( + this.beacon.upgradeTo(v2.address, { from: other }), + 'Ownable: caller is not the owner', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/transparent/ProxyAdmin.test.js b/lib/openzeppelin-contracts/test/proxy/transparent/ProxyAdmin.test.js new file mode 100644 index 0000000..07adba6 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/transparent/ProxyAdmin.test.js @@ -0,0 +1,125 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ImplV1 = artifacts.require('DummyImplementation'); +const ImplV2 = artifacts.require('DummyImplementationV2'); +const ProxyAdmin = artifacts.require('ProxyAdmin'); +const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy'); + +contract('ProxyAdmin', function (accounts) { + const [proxyAdminOwner, newAdmin, anotherAccount] = accounts; + + before('set implementations', async function () { + this.implementationV1 = await ImplV1.new(); + this.implementationV2 = await ImplV2.new(); + }); + + beforeEach(async function () { + const initializeData = Buffer.from(''); + this.proxyAdmin = await ProxyAdmin.new({ from: proxyAdminOwner }); + this.proxy = await TransparentUpgradeableProxy.new( + this.implementationV1.address, + this.proxyAdmin.address, + initializeData, + { from: proxyAdminOwner }, + ); + }); + + it('has an owner', async function () { + expect(await this.proxyAdmin.owner()).to.equal(proxyAdminOwner); + }); + + describe('#getProxyAdmin', function () { + it('returns proxyAdmin as admin of the proxy', async function () { + const admin = await this.proxyAdmin.getProxyAdmin(this.proxy.address); + expect(admin).to.be.equal(this.proxyAdmin.address); + }); + + it('call to invalid proxy', async function () { + await expectRevert.unspecified(this.proxyAdmin.getProxyAdmin(this.implementationV1.address)); + }); + }); + + describe('#changeProxyAdmin', function () { + it('fails to change proxy admin if its not the proxy owner', async function () { + await expectRevert( + this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: anotherAccount }), + 'caller is not the owner', + ); + }); + + it('changes proxy admin', async function () { + await this.proxyAdmin.changeProxyAdmin(this.proxy.address, newAdmin, { from: proxyAdminOwner }); + expect(await this.proxy.admin.call({ from: newAdmin })).to.eq(newAdmin); + }); + }); + + describe('#getProxyImplementation', function () { + it('returns proxy implementation address', async function () { + const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); + expect(implementationAddress).to.be.equal(this.implementationV1.address); + }); + + it('call to invalid proxy', async function () { + await expectRevert.unspecified(this.proxyAdmin.getProxyImplementation(this.implementationV1.address)); + }); + }); + + describe('#upgrade', function () { + context('with unauthorized account', function () { + it('fails to upgrade', async function () { + await expectRevert( + this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: anotherAccount }), + 'caller is not the owner', + ); + }); + }); + + context('with authorized account', function () { + it('upgrades implementation', async function () { + await this.proxyAdmin.upgrade(this.proxy.address, this.implementationV2.address, { from: proxyAdminOwner }); + const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); + expect(implementationAddress).to.be.equal(this.implementationV2.address); + }); + }); + }); + + describe('#upgradeAndCall', function () { + context('with unauthorized account', function () { + it('fails to upgrade', async function () { + const callData = new ImplV1('').contract.methods.initializeNonPayableWithValue(1337).encodeABI(); + await expectRevert( + this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, + { from: anotherAccount }, + ), + 'caller is not the owner', + ); + }); + }); + + context('with authorized account', function () { + context('with invalid callData', function () { + it('fails to upgrade', async function () { + const callData = '0x12345678'; + await expectRevert.unspecified( + this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, + { from: proxyAdminOwner }, + ), + ); + }); + }); + + context('with valid callData', function () { + it('upgrades implementation', async function () { + const callData = new ImplV1('').contract.methods.initializeNonPayableWithValue(1337).encodeABI(); + await this.proxyAdmin.upgradeAndCall(this.proxy.address, this.implementationV2.address, callData, + { from: proxyAdminOwner }, + ); + const implementationAddress = await this.proxyAdmin.getProxyImplementation(this.proxy.address); + expect(implementationAddress).to.be.equal(this.implementationV2.address); + }); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js b/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js new file mode 100644 index 0000000..33fef6f --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.behaviour.js @@ -0,0 +1,431 @@ +const { BN, expectRevert, expectEvent, constants } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; +const { getSlot, ImplementationSlot, AdminSlot } = require('../../helpers/erc1967'); + +const { expect } = require('chai'); + +const Proxy = artifacts.require('Proxy'); +const Implementation1 = artifacts.require('Implementation1'); +const Implementation2 = artifacts.require('Implementation2'); +const Implementation3 = artifacts.require('Implementation3'); +const Implementation4 = artifacts.require('Implementation4'); +const MigratableMockV1 = artifacts.require('MigratableMockV1'); +const MigratableMockV2 = artifacts.require('MigratableMockV2'); +const MigratableMockV3 = artifacts.require('MigratableMockV3'); +const InitializableMock = artifacts.require('InitializableMock'); +const DummyImplementation = artifacts.require('DummyImplementation'); +const ClashingImplementation = artifacts.require('ClashingImplementation'); + +module.exports = function shouldBehaveLikeTransparentUpgradeableProxy (createProxy, accounts) { + const [proxyAdminAddress, proxyAdminOwner, anotherAccount] = accounts; + + before(async function () { + this.implementationV0 = (await DummyImplementation.new()).address; + this.implementationV1 = (await DummyImplementation.new()).address; + }); + + beforeEach(async function () { + const initializeData = Buffer.from(''); + this.proxy = await createProxy(this.implementationV0, proxyAdminAddress, initializeData, { + from: proxyAdminOwner, + }); + this.proxyAddress = this.proxy.address; + }); + + describe('implementation', function () { + it('returns the current implementation address', async function () { + const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); + + expect(implementation).to.be.equal(this.implementationV0); + }); + + it('delegates to the implementation', async function () { + const dummy = new DummyImplementation(this.proxyAddress); + const value = await dummy.get(); + + expect(value).to.equal(true); + }); + }); + + describe('upgradeTo', function () { + describe('when the sender is the admin', function () { + const from = proxyAdminAddress; + + describe('when the given implementation is different from the current one', function () { + it('upgrades to the requested implementation', async function () { + await this.proxy.upgradeTo(this.implementationV1, { from }); + + const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); + expect(implementation).to.be.equal(this.implementationV1); + }); + + it('emits an event', async function () { + expectEvent( + await this.proxy.upgradeTo(this.implementationV1, { from }), + 'Upgraded', { + implementation: this.implementationV1, + }, + ); + }); + }); + + describe('when the given implementation is the zero address', function () { + it('reverts', async function () { + await expectRevert( + this.proxy.upgradeTo(ZERO_ADDRESS, { from }), + 'ERC1967: new implementation is not a contract', + ); + }); + }); + }); + + describe('when the sender is not the admin', function () { + const from = anotherAccount; + + it('reverts', async function () { + await expectRevert.unspecified( + this.proxy.upgradeTo(this.implementationV1, { from }), + ); + }); + }); + }); + + describe('upgradeToAndCall', function () { + describe('without migrations', function () { + beforeEach(async function () { + this.behavior = await InitializableMock.new(); + }); + + describe('when the call does not fail', function () { + const initializeData = new InitializableMock('').contract.methods['initializeWithX(uint256)'](42).encodeABI(); + + describe('when the sender is the admin', function () { + const from = proxyAdminAddress; + const value = 1e5; + + beforeEach(async function () { + this.receipt = await this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from, value }); + }); + + it('upgrades to the requested implementation', async function () { + const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); + expect(implementation).to.be.equal(this.behavior.address); + }); + + it('emits an event', function () { + expectEvent(this.receipt, 'Upgraded', { implementation: this.behavior.address }); + }); + + it('calls the initializer function', async function () { + const migratable = new InitializableMock(this.proxyAddress); + const x = await migratable.x(); + expect(x).to.be.bignumber.equal('42'); + }); + + it('sends given value to the proxy', async function () { + const balance = await web3.eth.getBalance(this.proxyAddress); + expect(balance.toString()).to.be.bignumber.equal(value.toString()); + }); + + it.skip('uses the storage of the proxy', async function () { + // storage layout should look as follows: + // - 0: Initializable storage + // - 1-50: Initailizable reserved storage (50 slots) + // - 51: initializerRan + // - 52: x + const storedValue = await Proxy.at(this.proxyAddress).getStorageAt(52); + expect(parseInt(storedValue)).to.eq(42); + }); + }); + + describe('when the sender is not the admin', function () { + it('reverts', async function () { + await expectRevert.unspecified( + this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from: anotherAccount }), + ); + }); + }); + }); + + describe('when the call does fail', function () { + const initializeData = new InitializableMock('').contract.methods.fail().encodeABI(); + + it('reverts', async function () { + await expectRevert.unspecified( + this.proxy.upgradeToAndCall(this.behavior.address, initializeData, { from: proxyAdminAddress }), + ); + }); + }); + }); + + describe('with migrations', function () { + describe('when the sender is the admin', function () { + const from = proxyAdminAddress; + const value = 1e5; + + describe('when upgrading to V1', function () { + const v1MigrationData = new MigratableMockV1('').contract.methods.initialize(42).encodeABI(); + + beforeEach(async function () { + this.behaviorV1 = await MigratableMockV1.new(); + this.balancePreviousV1 = new BN(await web3.eth.getBalance(this.proxyAddress)); + this.receipt = await this.proxy.upgradeToAndCall(this.behaviorV1.address, v1MigrationData, { from, value }); + }); + + it('upgrades to the requested version and emits an event', async function () { + const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); + expect(implementation).to.be.equal(this.behaviorV1.address); + expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV1.address }); + }); + + it('calls the \'initialize\' function and sends given value to the proxy', async function () { + const migratable = new MigratableMockV1(this.proxyAddress); + + const x = await migratable.x(); + expect(x).to.be.bignumber.equal('42'); + + const balance = await web3.eth.getBalance(this.proxyAddress); + expect(new BN(balance)).to.be.bignumber.equal(this.balancePreviousV1.addn(value)); + }); + + describe('when upgrading to V2', function () { + const v2MigrationData = new MigratableMockV2('').contract.methods.migrate(10, 42).encodeABI(); + + beforeEach(async function () { + this.behaviorV2 = await MigratableMockV2.new(); + this.balancePreviousV2 = new BN(await web3.eth.getBalance(this.proxyAddress)); + this.receipt = + await this.proxy.upgradeToAndCall(this.behaviorV2.address, v2MigrationData, { from, value }); + }); + + it('upgrades to the requested version and emits an event', async function () { + const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); + expect(implementation).to.be.equal(this.behaviorV2.address); + expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV2.address }); + }); + + it('calls the \'migrate\' function and sends given value to the proxy', async function () { + const migratable = new MigratableMockV2(this.proxyAddress); + + const x = await migratable.x(); + expect(x).to.be.bignumber.equal('10'); + + const y = await migratable.y(); + expect(y).to.be.bignumber.equal('42'); + + const balance = new BN(await web3.eth.getBalance(this.proxyAddress)); + expect(balance).to.be.bignumber.equal(this.balancePreviousV2.addn(value)); + }); + + describe('when upgrading to V3', function () { + const v3MigrationData = new MigratableMockV3('').contract.methods['migrate()']().encodeABI(); + + beforeEach(async function () { + this.behaviorV3 = await MigratableMockV3.new(); + this.balancePreviousV3 = new BN(await web3.eth.getBalance(this.proxyAddress)); + this.receipt = + await this.proxy.upgradeToAndCall(this.behaviorV3.address, v3MigrationData, { from, value }); + }); + + it('upgrades to the requested version and emits an event', async function () { + const implementation = await this.proxy.implementation.call({ from: proxyAdminAddress }); + expect(implementation).to.be.equal(this.behaviorV3.address); + expectEvent(this.receipt, 'Upgraded', { implementation: this.behaviorV3.address }); + }); + + it('calls the \'migrate\' function and sends given value to the proxy', async function () { + const migratable = new MigratableMockV3(this.proxyAddress); + + const x = await migratable.x(); + expect(x).to.be.bignumber.equal('42'); + + const y = await migratable.y(); + expect(y).to.be.bignumber.equal('10'); + + const balance = new BN(await web3.eth.getBalance(this.proxyAddress)); + expect(balance).to.be.bignumber.equal(this.balancePreviousV3.addn(value)); + }); + }); + }); + }); + }); + + describe('when the sender is not the admin', function () { + const from = anotherAccount; + + it('reverts', async function () { + const behaviorV1 = await MigratableMockV1.new(); + const v1MigrationData = new MigratableMockV1('').contract.methods.initialize(42).encodeABI(); + await expectRevert.unspecified( + this.proxy.upgradeToAndCall(behaviorV1.address, v1MigrationData, { from }), + ); + }); + }); + }); + }); + + describe('changeAdmin', function () { + describe('when the new proposed admin is not the zero address', function () { + const newAdmin = anotherAccount; + + describe('when the sender is the admin', function () { + beforeEach('transferring', async function () { + this.receipt = await this.proxy.changeAdmin(newAdmin, { from: proxyAdminAddress }); + }); + + it('assigns new proxy admin', async function () { + const newProxyAdmin = await this.proxy.admin.call({ from: newAdmin }); + expect(newProxyAdmin).to.be.equal(anotherAccount); + }); + + it('emits an event', function () { + expectEvent(this.receipt, 'AdminChanged', { + previousAdmin: proxyAdminAddress, + newAdmin: newAdmin, + }); + }); + }); + + describe('when the sender is not the admin', function () { + it('reverts', async function () { + await expectRevert.unspecified(this.proxy.changeAdmin(newAdmin, { from: anotherAccount })); + }); + }); + }); + + describe('when the new proposed admin is the zero address', function () { + it('reverts', async function () { + await expectRevert( + this.proxy.changeAdmin(ZERO_ADDRESS, { from: proxyAdminAddress }), + 'ERC1967: new admin is the zero address', + ); + }); + }); + }); + + describe('storage', function () { + it('should store the implementation address in specified location', async function () { + const implementationSlot = await getSlot(this.proxy, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.equal(this.implementationV0); + }); + + it('should store the admin proxy in specified location', async function () { + const proxyAdminSlot = await getSlot(this.proxy, AdminSlot); + const proxyAdminAddress = web3.utils.toChecksumAddress(proxyAdminSlot.substr(-40)); + expect(proxyAdminAddress).to.be.equal(proxyAdminAddress); + }); + }); + + describe('transparent proxy', function () { + beforeEach('creating proxy', async function () { + const initializeData = Buffer.from(''); + this.impl = await ClashingImplementation.new(); + this.proxy = await createProxy(this.impl.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner }); + + this.clashing = new ClashingImplementation(this.proxy.address); + }); + + it('proxy admin cannot call delegated functions', async function () { + await expectRevert( + this.clashing.delegatedFunction({ from: proxyAdminAddress }), + 'TransparentUpgradeableProxy: admin cannot fallback to proxy target', + ); + }); + + context('when function names clash', function () { + it('when sender is proxy admin should run the proxy function', async function () { + const value = await this.proxy.admin.call({ from: proxyAdminAddress }); + expect(value).to.be.equal(proxyAdminAddress); + }); + + it('when sender is other should delegate to implementation', async function () { + const value = await this.proxy.admin.call({ from: anotherAccount }); + expect(value).to.be.equal('0x0000000000000000000000000000000011111142'); + }); + }); + }); + + describe('regression', () => { + const initializeData = Buffer.from(''); + + it('should add new function', async () => { + const instance1 = await Implementation1.new(); + const proxy = await createProxy(instance1.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner }); + + const proxyInstance1 = new Implementation1(proxy.address); + await proxyInstance1.setValue(42); + + const instance2 = await Implementation2.new(); + await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress }); + + const proxyInstance2 = new Implementation2(proxy.address); + const res = await proxyInstance2.getValue(); + expect(res.toString()).to.eq('42'); + }); + + it('should remove function', async () => { + const instance2 = await Implementation2.new(); + const proxy = await createProxy(instance2.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner }); + + const proxyInstance2 = new Implementation2(proxy.address); + await proxyInstance2.setValue(42); + const res = await proxyInstance2.getValue(); + expect(res.toString()).to.eq('42'); + + const instance1 = await Implementation1.new(); + await proxy.upgradeTo(instance1.address, { from: proxyAdminAddress }); + + const proxyInstance1 = new Implementation2(proxy.address); + await expectRevert.unspecified(proxyInstance1.getValue()); + }); + + it('should change function signature', async () => { + const instance1 = await Implementation1.new(); + const proxy = await createProxy(instance1.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner }); + + const proxyInstance1 = new Implementation1(proxy.address); + await proxyInstance1.setValue(42); + + const instance3 = await Implementation3.new(); + await proxy.upgradeTo(instance3.address, { from: proxyAdminAddress }); + const proxyInstance3 = new Implementation3(proxy.address); + + const res = await proxyInstance3.getValue(8); + expect(res.toString()).to.eq('50'); + }); + + it('should add fallback function', async () => { + const initializeData = Buffer.from(''); + const instance1 = await Implementation1.new(); + const proxy = await createProxy(instance1.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner }); + + const instance4 = await Implementation4.new(); + await proxy.upgradeTo(instance4.address, { from: proxyAdminAddress }); + const proxyInstance4 = new Implementation4(proxy.address); + + const data = '0x'; + await web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }); + + const res = await proxyInstance4.getValue(); + expect(res.toString()).to.eq('1'); + }); + + it('should remove fallback function', async () => { + const instance4 = await Implementation4.new(); + const proxy = await createProxy(instance4.address, proxyAdminAddress, initializeData, { from: proxyAdminOwner }); + + const instance2 = await Implementation2.new(); + await proxy.upgradeTo(instance2.address, { from: proxyAdminAddress }); + + const data = '0x'; + await expectRevert.unspecified( + web3.eth.sendTransaction({ to: proxy.address, from: anotherAccount, data }), + ); + + const proxyInstance2 = new Implementation2(proxy.address); + const res = await proxyInstance2.getValue(); + expect(res.toString()).to.eq('0'); + }); + }); +}; diff --git a/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.test.js b/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.test.js new file mode 100644 index 0000000..86dd55d --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/transparent/TransparentUpgradeableProxy.test.js @@ -0,0 +1,15 @@ +const shouldBehaveLikeProxy = require('../Proxy.behaviour'); +const shouldBehaveLikeTransparentUpgradeableProxy = require('./TransparentUpgradeableProxy.behaviour'); + +const TransparentUpgradeableProxy = artifacts.require('TransparentUpgradeableProxy'); + +contract('TransparentUpgradeableProxy', function (accounts) { + const [proxyAdminAddress, proxyAdminOwner] = accounts; + + const createProxy = async function (logic, admin, initData, opts) { + return TransparentUpgradeableProxy.new(logic, admin, initData, opts); + }; + + shouldBehaveLikeProxy(createProxy, proxyAdminAddress, proxyAdminOwner); + shouldBehaveLikeTransparentUpgradeableProxy(createProxy, accounts); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/utils/Initializable.test.js b/lib/openzeppelin-contracts/test/proxy/utils/Initializable.test.js new file mode 100644 index 0000000..2c0c147 --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/utils/Initializable.test.js @@ -0,0 +1,218 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const InitializableMock = artifacts.require('InitializableMock'); +const ConstructorInitializableMock = artifacts.require('ConstructorInitializableMock'); +const ChildConstructorInitializableMock = artifacts.require('ChildConstructorInitializableMock'); +const ReinitializerMock = artifacts.require('ReinitializerMock'); +const SampleChild = artifacts.require('SampleChild'); +const DisableBad1 = artifacts.require('DisableBad1'); +const DisableBad2 = artifacts.require('DisableBad2'); +const DisableOk = artifacts.require('DisableOk'); + +contract('Initializable', function (accounts) { + describe('basic testing without inheritance', function () { + beforeEach('deploying', async function () { + this.contract = await InitializableMock.new(); + }); + + describe('before initialize', function () { + it('initializer has not run', async function () { + expect(await this.contract.initializerRan()).to.equal(false); + }); + + it('_initializing returns false before initialization', async function () { + expect(await this.contract.isInitializing()).to.equal(false); + }); + }); + + describe('after initialize', function () { + beforeEach('initializing', async function () { + await this.contract.initialize(); + }); + + it('initializer has run', async function () { + expect(await this.contract.initializerRan()).to.equal(true); + }); + + it('_initializing returns false after initialization', async function () { + expect(await this.contract.isInitializing()).to.equal(false); + }); + + it('initializer does not run again', async function () { + await expectRevert(this.contract.initialize(), 'Initializable: contract is already initialized'); + }); + }); + + describe('nested under an initializer', function () { + it('initializer modifier reverts', async function () { + await expectRevert(this.contract.initializerNested(), 'Initializable: contract is already initialized'); + }); + + it('onlyInitializing modifier succeeds', async function () { + await this.contract.onlyInitializingNested(); + expect(await this.contract.onlyInitializingRan()).to.equal(true); + }); + }); + + it('cannot call onlyInitializable function outside the scope of an initializable function', async function () { + await expectRevert(this.contract.initializeOnlyInitializing(), 'Initializable: contract is not initializing'); + }); + }); + + it('nested initializer can run during construction', async function () { + const contract2 = await ConstructorInitializableMock.new(); + expect(await contract2.initializerRan()).to.equal(true); + expect(await contract2.onlyInitializingRan()).to.equal(true); + }); + + it('multiple constructor levels can be initializers', async function () { + const contract2 = await ChildConstructorInitializableMock.new(); + expect(await contract2.initializerRan()).to.equal(true); + expect(await contract2.childInitializerRan()).to.equal(true); + expect(await contract2.onlyInitializingRan()).to.equal(true); + }); + + describe('reinitialization', function () { + beforeEach('deploying', async function () { + this.contract = await ReinitializerMock.new(); + }); + + it('can reinitialize', async function () { + expect(await this.contract.counter()).to.be.bignumber.equal('0'); + await this.contract.initialize(); + expect(await this.contract.counter()).to.be.bignumber.equal('1'); + await this.contract.reinitialize(2); + expect(await this.contract.counter()).to.be.bignumber.equal('2'); + await this.contract.reinitialize(3); + expect(await this.contract.counter()).to.be.bignumber.equal('3'); + }); + + it('can jump multiple steps', async function () { + expect(await this.contract.counter()).to.be.bignumber.equal('0'); + await this.contract.initialize(); + expect(await this.contract.counter()).to.be.bignumber.equal('1'); + await this.contract.reinitialize(128); + expect(await this.contract.counter()).to.be.bignumber.equal('2'); + }); + + it('cannot nest reinitializers', async function () { + expect(await this.contract.counter()).to.be.bignumber.equal('0'); + await expectRevert(this.contract.nestedReinitialize(2, 2), 'Initializable: contract is already initialized'); + await expectRevert(this.contract.nestedReinitialize(2, 3), 'Initializable: contract is already initialized'); + await expectRevert(this.contract.nestedReinitialize(3, 2), 'Initializable: contract is already initialized'); + }); + + it('can chain reinitializers', async function () { + expect(await this.contract.counter()).to.be.bignumber.equal('0'); + await this.contract.chainReinitialize(2, 3); + expect(await this.contract.counter()).to.be.bignumber.equal('2'); + }); + + it('_getInitializedVersion returns right version', async function () { + await this.contract.initialize(); + expect(await this.contract.getInitializedVersion()).to.be.bignumber.equal('1'); + await this.contract.reinitialize(12); + expect(await this.contract.getInitializedVersion()).to.be.bignumber.equal('12'); + }); + + describe('contract locking', function () { + it('prevents initialization', async function () { + await this.contract.disableInitializers(); + await expectRevert(this.contract.initialize(), 'Initializable: contract is already initialized'); + }); + + it('prevents re-initialization', async function () { + await this.contract.disableInitializers(); + await expectRevert(this.contract.reinitialize(255), 'Initializable: contract is already initialized'); + }); + + it('can lock contract after initialization', async function () { + await this.contract.initialize(); + await this.contract.disableInitializers(); + await expectRevert(this.contract.reinitialize(255), 'Initializable: contract is already initialized'); + }); + }); + }); + + describe('events', function () { + it('constructor initialization emits event', async function () { + const contract = await ConstructorInitializableMock.new(); + + await expectEvent.inTransaction(contract.transactionHash, contract, 'Initialized', { version: '1' }); + }); + + it('initialization emits event', async function () { + const contract = await ReinitializerMock.new(); + + const { receipt } = await contract.initialize(); + expect(receipt.logs.filter(({ event }) => event === 'Initialized').length).to.be.equal(1); + expectEvent(receipt, 'Initialized', { version: '1' }); + }); + + it('reinitialization emits event', async function () { + const contract = await ReinitializerMock.new(); + + const { receipt } = await contract.reinitialize(128); + expect(receipt.logs.filter(({ event }) => event === 'Initialized').length).to.be.equal(1); + expectEvent(receipt, 'Initialized', { version: '128' }); + }); + + it('chained reinitialization emits multiple events', async function () { + const contract = await ReinitializerMock.new(); + + const { receipt } = await contract.chainReinitialize(2, 3); + expect(receipt.logs.filter(({ event }) => event === 'Initialized').length).to.be.equal(2); + expectEvent(receipt, 'Initialized', { version: '2' }); + expectEvent(receipt, 'Initialized', { version: '3' }); + }); + }); + + describe('complex testing with inheritance', function () { + const mother = '12'; + const gramps = '56'; + const father = '34'; + const child = '78'; + + beforeEach('deploying', async function () { + this.contract = await SampleChild.new(); + }); + + beforeEach('initializing', async function () { + await this.contract.initialize(mother, gramps, father, child); + }); + + it('initializes human', async function () { + expect(await this.contract.isHuman()).to.be.equal(true); + }); + + it('initializes mother', async function () { + expect(await this.contract.mother()).to.be.bignumber.equal(mother); + }); + + it('initializes gramps', async function () { + expect(await this.contract.gramps()).to.be.bignumber.equal(gramps); + }); + + it('initializes father', async function () { + expect(await this.contract.father()).to.be.bignumber.equal(father); + }); + + it('initializes child', async function () { + expect(await this.contract.child()).to.be.bignumber.equal(child); + }); + }); + + describe('disabling initialization', function () { + it('old and new patterns in bad sequence', async function () { + await expectRevert(DisableBad1.new(), 'Initializable: contract is already initialized'); + await expectRevert(DisableBad2.new(), 'Initializable: contract is initializing'); + }); + + it('old and new patterns in good sequence', async function () { + const ok = await DisableOk.new(); + await expectEvent.inConstruction(ok, 'Initialized', { version: '1' }); + await expectEvent.inConstruction(ok, 'Initialized', { version: '255' }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/proxy/utils/UUPSUpgradeable.test.js b/lib/openzeppelin-contracts/test/proxy/utils/UUPSUpgradeable.test.js new file mode 100644 index 0000000..466081d --- /dev/null +++ b/lib/openzeppelin-contracts/test/proxy/utils/UUPSUpgradeable.test.js @@ -0,0 +1,85 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { web3 } = require('@openzeppelin/test-helpers/src/setup'); +const { getSlot, ImplementationSlot } = require('../../helpers/erc1967'); + +const ERC1967Proxy = artifacts.require('ERC1967Proxy'); +const UUPSUpgradeableMock = artifacts.require('UUPSUpgradeableMock'); +const UUPSUpgradeableUnsafeMock = artifacts.require('UUPSUpgradeableUnsafeMock'); +const UUPSUpgradeableLegacyMock = artifacts.require('UUPSUpgradeableLegacyMock'); +const CountersImpl = artifacts.require('CountersImpl'); + +contract('UUPSUpgradeable', function (accounts) { + before(async function () { + this.implInitial = await UUPSUpgradeableMock.new(); + this.implUpgradeOk = await UUPSUpgradeableMock.new(); + this.implUpgradeUnsafe = await UUPSUpgradeableUnsafeMock.new(); + this.implUpgradeNonUUPS = await CountersImpl.new(); + }); + + beforeEach(async function () { + const { address } = await ERC1967Proxy.new(this.implInitial.address, '0x'); + this.instance = await UUPSUpgradeableMock.at(address); + }); + + it('upgrade to upgradeable implementation', async function () { + const { receipt } = await this.instance.upgradeTo(this.implUpgradeOk.address); + expect(receipt.logs.filter(({ event }) => event === 'Upgraded').length).to.be.equal(1); + expectEvent(receipt, 'Upgraded', { implementation: this.implUpgradeOk.address }); + }); + + it('upgrade to upgradeable implementation with call', async function () { + expect(await this.instance.current()).to.be.bignumber.equal('0'); + + const { receipt } = await this.instance.upgradeToAndCall( + this.implUpgradeOk.address, + this.implUpgradeOk.contract.methods.increment().encodeABI(), + ); + expect(receipt.logs.filter(({ event }) => event === 'Upgraded').length).to.be.equal(1); + expectEvent(receipt, 'Upgraded', { implementation: this.implUpgradeOk.address }); + + expect(await this.instance.current()).to.be.bignumber.equal('1'); + }); + + it('upgrade to and unsafe upgradeable implementation', async function () { + const { receipt } = await this.instance.upgradeTo(this.implUpgradeUnsafe.address); + expectEvent(receipt, 'Upgraded', { implementation: this.implUpgradeUnsafe.address }); + }); + + // delegate to a non existing upgradeTo function causes a low level revert + it('reject upgrade to non uups implementation', async function () { + await expectRevert( + this.instance.upgradeTo(this.implUpgradeNonUUPS.address), + 'ERC1967Upgrade: new implementation is not UUPS', + ); + }); + + it('reject proxy address as implementation', async function () { + const { address } = await ERC1967Proxy.new(this.implInitial.address, '0x'); + const otherInstance = await UUPSUpgradeableMock.at(address); + + await expectRevert( + this.instance.upgradeTo(otherInstance.address), + 'ERC1967Upgrade: new implementation is not UUPS', + ); + }); + + it('can upgrade from legacy implementations', async function () { + const legacyImpl = await UUPSUpgradeableLegacyMock.new(); + const legacyInstance = await ERC1967Proxy.new(legacyImpl.address, '0x') + .then(({ address }) => UUPSUpgradeableLegacyMock.at(address)); + + const receipt = await legacyInstance.upgradeTo(this.implInitial.address); + + const UpgradedEvents = receipt.logs.filter(({ address, event }) => + address === legacyInstance.address && + event === 'Upgraded', + ); + expect(UpgradedEvents.length).to.be.equal(1); + + expectEvent(receipt, 'Upgraded', { implementation: this.implInitial.address }); + + const implementationSlot = await getSlot(legacyInstance, ImplementationSlot); + const implementationAddress = web3.utils.toChecksumAddress(implementationSlot.substr(-40)); + expect(implementationAddress).to.be.equal(this.implInitial.address); + }); +}); diff --git a/lib/openzeppelin-contracts/test/security/Pausable.test.js b/lib/openzeppelin-contracts/test/security/Pausable.test.js new file mode 100644 index 0000000..86701bc --- /dev/null +++ b/lib/openzeppelin-contracts/test/security/Pausable.test.js @@ -0,0 +1,89 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const PausableMock = artifacts.require('PausableMock'); + +contract('Pausable', function (accounts) { + const [ pauser ] = accounts; + + beforeEach(async function () { + this.pausable = await PausableMock.new(); + }); + + context('when unpaused', function () { + beforeEach(async function () { + expect(await this.pausable.paused()).to.equal(false); + }); + + it('can perform normal process in non-pause', async function () { + expect(await this.pausable.count()).to.be.bignumber.equal('0'); + + await this.pausable.normalProcess(); + expect(await this.pausable.count()).to.be.bignumber.equal('1'); + }); + + it('cannot take drastic measure in non-pause', async function () { + await expectRevert(this.pausable.drasticMeasure(), + 'Pausable: not paused', + ); + expect(await this.pausable.drasticMeasureTaken()).to.equal(false); + }); + + context('when paused', function () { + beforeEach(async function () { + (this.receipt = await this.pausable.pause({ from: pauser })); + }); + + it('emits a Paused event', function () { + expectEvent(this.receipt, 'Paused', { account: pauser }); + }); + + it('cannot perform normal process in pause', async function () { + await expectRevert(this.pausable.normalProcess(), 'Pausable: paused'); + }); + + it('can take a drastic measure in a pause', async function () { + await this.pausable.drasticMeasure(); + expect(await this.pausable.drasticMeasureTaken()).to.equal(true); + }); + + it('reverts when re-pausing', async function () { + await expectRevert(this.pausable.pause(), 'Pausable: paused'); + }); + + describe('unpausing', function () { + it('is unpausable by the pauser', async function () { + await this.pausable.unpause(); + expect(await this.pausable.paused()).to.equal(false); + }); + + context('when unpaused', function () { + beforeEach(async function () { + (this.receipt = await this.pausable.unpause({ from: pauser })); + }); + + it('emits an Unpaused event', function () { + expectEvent(this.receipt, 'Unpaused', { account: pauser }); + }); + + it('should resume allowing normal process', async function () { + expect(await this.pausable.count()).to.be.bignumber.equal('0'); + await this.pausable.normalProcess(); + expect(await this.pausable.count()).to.be.bignumber.equal('1'); + }); + + it('should prevent drastic measure', async function () { + await expectRevert(this.pausable.drasticMeasure(), + 'Pausable: not paused', + ); + }); + + it('reverts when re-unpausing', async function () { + await expectRevert(this.pausable.unpause(), 'Pausable: not paused'); + }); + }); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/security/PullPayment.test.js b/lib/openzeppelin-contracts/test/security/PullPayment.test.js new file mode 100644 index 0000000..1552ed9 --- /dev/null +++ b/lib/openzeppelin-contracts/test/security/PullPayment.test.js @@ -0,0 +1,51 @@ +const { balance, ether } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const PullPaymentMock = artifacts.require('PullPaymentMock'); + +contract('PullPayment', function (accounts) { + const [ payer, payee1, payee2 ] = accounts; + + const amount = ether('17'); + + beforeEach(async function () { + this.contract = await PullPaymentMock.new({ value: amount }); + }); + + describe('payments', function () { + it('can record an async payment correctly', async function () { + await this.contract.callTransfer(payee1, 100, { from: payer }); + expect(await this.contract.payments(payee1)).to.be.bignumber.equal('100'); + }); + + it('can add multiple balances on one account', async function () { + await this.contract.callTransfer(payee1, 200, { from: payer }); + await this.contract.callTransfer(payee1, 300, { from: payer }); + expect(await this.contract.payments(payee1)).to.be.bignumber.equal('500'); + }); + + it('can add balances on multiple accounts', async function () { + await this.contract.callTransfer(payee1, 200, { from: payer }); + await this.contract.callTransfer(payee2, 300, { from: payer }); + + expect(await this.contract.payments(payee1)).to.be.bignumber.equal('200'); + + expect(await this.contract.payments(payee2)).to.be.bignumber.equal('300'); + }); + }); + + describe('withdrawPayments', function () { + it('can withdraw payment', async function () { + const balanceTracker = await balance.tracker(payee1); + + await this.contract.callTransfer(payee1, amount, { from: payer }); + expect(await this.contract.payments(payee1)).to.be.bignumber.equal(amount); + + await this.contract.withdrawPayments(payee1); + + expect(await balanceTracker.delta()).to.be.bignumber.equal(amount); + expect(await this.contract.payments(payee1)).to.be.bignumber.equal('0'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/security/ReentrancyGuard.test.js b/lib/openzeppelin-contracts/test/security/ReentrancyGuard.test.js new file mode 100644 index 0000000..c7f7d46 --- /dev/null +++ b/lib/openzeppelin-contracts/test/security/ReentrancyGuard.test.js @@ -0,0 +1,48 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ReentrancyMock = artifacts.require('ReentrancyMock'); +const ReentrancyAttack = artifacts.require('ReentrancyAttack'); + +contract('ReentrancyGuard', function (accounts) { + beforeEach(async function () { + this.reentrancyMock = await ReentrancyMock.new(); + expect(await this.reentrancyMock.counter()).to.be.bignumber.equal('0'); + }); + + it('nonReentrant function can be called', async function () { + expect(await this.reentrancyMock.counter()).to.be.bignumber.equal('0'); + await this.reentrancyMock.callback(); + expect(await this.reentrancyMock.counter()).to.be.bignumber.equal('1'); + }); + + it('does not allow remote callback', async function () { + const attacker = await ReentrancyAttack.new(); + await expectRevert( + this.reentrancyMock.countAndCall(attacker.address), 'ReentrancyAttack: failed call'); + }); + + it('_reentrancyGuardEntered should be true when guarded', async function () { + await this.reentrancyMock.guardedCheckEntered(); + }); + + it('_reentrancyGuardEntered should be false when unguarded', async function () { + await this.reentrancyMock.unguardedCheckNotEntered(); + }); + + // The following are more side-effects than intended behavior: + // I put them here as documentation, and to monitor any changes + // in the side-effects. + it('does not allow local recursion', async function () { + await expectRevert( + this.reentrancyMock.countLocalRecursive(10), 'ReentrancyGuard: reentrant call', + ); + }); + + it('does not allow indirect local recursion', async function () { + await expectRevert( + this.reentrancyMock.countThisRecursive(10), 'ReentrancyMock: failed call', + ); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.behavior.js b/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.behavior.js new file mode 100644 index 0000000..62ba666 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.behavior.js @@ -0,0 +1,774 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); + +const ERC1155ReceiverMock = artifacts.require('ERC1155ReceiverMock'); + +function shouldBehaveLikeERC1155 ([minter, firstTokenHolder, secondTokenHolder, multiTokenHolder, recipient, proxy]) { + const firstTokenId = new BN(1); + const secondTokenId = new BN(2); + const unknownTokenId = new BN(3); + + const firstAmount = new BN(1000); + const secondAmount = new BN(2000); + + const RECEIVER_SINGLE_MAGIC_VALUE = '0xf23a6e61'; + const RECEIVER_BATCH_MAGIC_VALUE = '0xbc197c81'; + + describe('like an ERC1155', function () { + describe('balanceOf', function () { + it('reverts when queried about the zero address', async function () { + await expectRevert( + this.token.balanceOf(ZERO_ADDRESS, firstTokenId), + 'ERC1155: address zero is not a valid owner', + ); + }); + + context('when accounts don\'t own tokens', function () { + it('returns zero for given addresses', async function () { + expect(await this.token.balanceOf( + firstTokenHolder, + firstTokenId, + )).to.be.bignumber.equal('0'); + + expect(await this.token.balanceOf( + secondTokenHolder, + secondTokenId, + )).to.be.bignumber.equal('0'); + + expect(await this.token.balanceOf( + firstTokenHolder, + unknownTokenId, + )).to.be.bignumber.equal('0'); + }); + }); + + context('when accounts own some tokens', function () { + beforeEach(async function () { + await this.token.mint(firstTokenHolder, firstTokenId, firstAmount, '0x', { + from: minter, + }); + await this.token.mint( + secondTokenHolder, + secondTokenId, + secondAmount, + '0x', + { + from: minter, + }, + ); + }); + + it('returns the amount of tokens owned by the given addresses', async function () { + expect(await this.token.balanceOf( + firstTokenHolder, + firstTokenId, + )).to.be.bignumber.equal(firstAmount); + + expect(await this.token.balanceOf( + secondTokenHolder, + secondTokenId, + )).to.be.bignumber.equal(secondAmount); + + expect(await this.token.balanceOf( + firstTokenHolder, + unknownTokenId, + )).to.be.bignumber.equal('0'); + }); + }); + }); + + describe('balanceOfBatch', function () { + it('reverts when input arrays don\'t match up', async function () { + await expectRevert( + this.token.balanceOfBatch( + [firstTokenHolder, secondTokenHolder, firstTokenHolder, secondTokenHolder], + [firstTokenId, secondTokenId, unknownTokenId], + ), + 'ERC1155: accounts and ids length mismatch', + ); + + await expectRevert( + this.token.balanceOfBatch( + [firstTokenHolder, secondTokenHolder], + [firstTokenId, secondTokenId, unknownTokenId], + ), + 'ERC1155: accounts and ids length mismatch', + ); + }); + + it('reverts when one of the addresses is the zero address', async function () { + await expectRevert( + this.token.balanceOfBatch( + [firstTokenHolder, secondTokenHolder, ZERO_ADDRESS], + [firstTokenId, secondTokenId, unknownTokenId], + ), + 'ERC1155: address zero is not a valid owner', + ); + }); + + context('when accounts don\'t own tokens', function () { + it('returns zeros for each account', async function () { + const result = await this.token.balanceOfBatch( + [firstTokenHolder, secondTokenHolder, firstTokenHolder], + [firstTokenId, secondTokenId, unknownTokenId], + ); + expect(result).to.be.an('array'); + expect(result[0]).to.be.a.bignumber.equal('0'); + expect(result[1]).to.be.a.bignumber.equal('0'); + expect(result[2]).to.be.a.bignumber.equal('0'); + }); + }); + + context('when accounts own some tokens', function () { + beforeEach(async function () { + await this.token.mint(firstTokenHolder, firstTokenId, firstAmount, '0x', { + from: minter, + }); + await this.token.mint( + secondTokenHolder, + secondTokenId, + secondAmount, + '0x', + { + from: minter, + }, + ); + }); + + it('returns amounts owned by each account in order passed', async function () { + const result = await this.token.balanceOfBatch( + [secondTokenHolder, firstTokenHolder, firstTokenHolder], + [secondTokenId, firstTokenId, unknownTokenId], + ); + expect(result).to.be.an('array'); + expect(result[0]).to.be.a.bignumber.equal(secondAmount); + expect(result[1]).to.be.a.bignumber.equal(firstAmount); + expect(result[2]).to.be.a.bignumber.equal('0'); + }); + + it('returns multiple times the balance of the same address when asked', async function () { + const result = await this.token.balanceOfBatch( + [firstTokenHolder, secondTokenHolder, firstTokenHolder], + [firstTokenId, secondTokenId, firstTokenId], + ); + expect(result).to.be.an('array'); + expect(result[0]).to.be.a.bignumber.equal(result[2]); + expect(result[0]).to.be.a.bignumber.equal(firstAmount); + expect(result[1]).to.be.a.bignumber.equal(secondAmount); + expect(result[2]).to.be.a.bignumber.equal(firstAmount); + }); + }); + }); + + describe('setApprovalForAll', function () { + let receipt; + beforeEach(async function () { + (receipt = await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder })); + }); + + it('sets approval status which can be queried via isApprovedForAll', async function () { + expect(await this.token.isApprovedForAll(multiTokenHolder, proxy)).to.be.equal(true); + }); + + it('emits an ApprovalForAll log', function () { + expectEvent(receipt, 'ApprovalForAll', { account: multiTokenHolder, operator: proxy, approved: true }); + }); + + it('can unset approval for an operator', async function () { + await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder }); + expect(await this.token.isApprovedForAll(multiTokenHolder, proxy)).to.be.equal(false); + }); + + it('reverts if attempting to approve self as an operator', async function () { + await expectRevert( + this.token.setApprovalForAll(multiTokenHolder, true, { from: multiTokenHolder }), + 'ERC1155: setting approval status for self', + ); + }); + }); + + describe('safeTransferFrom', function () { + beforeEach(async function () { + await this.token.mint(multiTokenHolder, firstTokenId, firstAmount, '0x', { + from: minter, + }); + await this.token.mint( + multiTokenHolder, + secondTokenId, + secondAmount, + '0x', + { + from: minter, + }, + ); + }); + + it('reverts when transferring more than balance', async function () { + await expectRevert( + this.token.safeTransferFrom( + multiTokenHolder, + recipient, + firstTokenId, + firstAmount.addn(1), + '0x', + { from: multiTokenHolder }, + ), + 'ERC1155: insufficient balance for transfer', + ); + }); + + it('reverts when transferring to zero address', async function () { + await expectRevert( + this.token.safeTransferFrom( + multiTokenHolder, + ZERO_ADDRESS, + firstTokenId, + firstAmount, + '0x', + { from: multiTokenHolder }, + ), + 'ERC1155: transfer to the zero address', + ); + }); + + function transferWasSuccessful ({ operator, from, id, value }) { + it('debits transferred balance from sender', async function () { + const newBalance = await this.token.balanceOf(from, id); + expect(newBalance).to.be.a.bignumber.equal('0'); + }); + + it('credits transferred balance to receiver', async function () { + const newBalance = await this.token.balanceOf(this.toWhom, id); + expect(newBalance).to.be.a.bignumber.equal(value); + }); + + it('emits a TransferSingle log', function () { + expectEvent(this.transferLogs, 'TransferSingle', { + operator, + from, + to: this.toWhom, + id, + value, + }); + }); + } + + context('when called by the multiTokenHolder', async function () { + beforeEach(async function () { + this.toWhom = recipient; + (this.transferLogs = + await this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstAmount, '0x', { + from: multiTokenHolder, + })); + }); + + transferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + id: firstTokenId, + value: firstAmount, + }); + + it('preserves existing balances which are not transferred by multiTokenHolder', async function () { + const balance1 = await this.token.balanceOf(multiTokenHolder, secondTokenId); + expect(balance1).to.be.a.bignumber.equal(secondAmount); + + const balance2 = await this.token.balanceOf(recipient, secondTokenId); + expect(balance2).to.be.a.bignumber.equal('0'); + }); + }); + + context('when called by an operator on behalf of the multiTokenHolder', function () { + context('when operator is not approved by multiTokenHolder', function () { + beforeEach(async function () { + await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder }); + }); + + it('reverts', async function () { + await expectRevert( + this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstAmount, '0x', { + from: proxy, + }), + 'ERC1155: caller is not token owner or approved', + ); + }); + }); + + context('when operator is approved by multiTokenHolder', function () { + beforeEach(async function () { + this.toWhom = recipient; + await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder }); + (this.transferLogs = + await this.token.safeTransferFrom(multiTokenHolder, recipient, firstTokenId, firstAmount, '0x', { + from: proxy, + })); + }); + + transferWasSuccessful.call(this, { + operator: proxy, + from: multiTokenHolder, + id: firstTokenId, + value: firstAmount, + }); + + it('preserves operator\'s balances not involved in the transfer', async function () { + const balance1 = await this.token.balanceOf(proxy, firstTokenId); + expect(balance1).to.be.a.bignumber.equal('0'); + + const balance2 = await this.token.balanceOf(proxy, secondTokenId); + expect(balance2).to.be.a.bignumber.equal('0'); + }); + }); + }); + + context('when sending to a valid receiver', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + RECEIVER_SINGLE_MAGIC_VALUE, false, + RECEIVER_BATCH_MAGIC_VALUE, false, + ); + }); + + context('without data', function () { + beforeEach(async function () { + this.toWhom = this.receiver.address; + this.transferReceipt = await this.token.safeTransferFrom( + multiTokenHolder, + this.receiver.address, + firstTokenId, + firstAmount, + '0x', + { from: multiTokenHolder }, + ); + (this.transferLogs = this.transferReceipt); + }); + + transferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + id: firstTokenId, + value: firstAmount, + }); + + it('calls onERC1155Received', async function () { + await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', { + operator: multiTokenHolder, + from: multiTokenHolder, + id: firstTokenId, + value: firstAmount, + data: null, + }); + }); + }); + + context('with data', function () { + const data = '0xf00dd00d'; + beforeEach(async function () { + this.toWhom = this.receiver.address; + this.transferReceipt = await this.token.safeTransferFrom( + multiTokenHolder, + this.receiver.address, + firstTokenId, + firstAmount, + data, + { from: multiTokenHolder }, + ); + (this.transferLogs = this.transferReceipt); + }); + + transferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + id: firstTokenId, + value: firstAmount, + }); + + it('calls onERC1155Received', async function () { + await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'Received', { + operator: multiTokenHolder, + from: multiTokenHolder, + id: firstTokenId, + value: firstAmount, + data, + }); + }); + }); + }); + + context('to a receiver contract returning unexpected value', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + '0x00c0ffee', false, + RECEIVER_BATCH_MAGIC_VALUE, false, + ); + }); + + it('reverts', async function () { + await expectRevert( + this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', { + from: multiTokenHolder, + }), + 'ERC1155: ERC1155Receiver rejected tokens', + ); + }); + }); + + context('to a receiver contract that reverts', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + RECEIVER_SINGLE_MAGIC_VALUE, true, + RECEIVER_BATCH_MAGIC_VALUE, false, + ); + }); + + it('reverts', async function () { + await expectRevert( + this.token.safeTransferFrom(multiTokenHolder, this.receiver.address, firstTokenId, firstAmount, '0x', { + from: multiTokenHolder, + }), + 'ERC1155ReceiverMock: reverting on receive', + ); + }); + }); + + context('to a contract that does not implement the required function', function () { + it('reverts', async function () { + const invalidReceiver = this.token; + await expectRevert.unspecified( + this.token.safeTransferFrom(multiTokenHolder, invalidReceiver.address, firstTokenId, firstAmount, '0x', { + from: multiTokenHolder, + }), + ); + }); + }); + }); + + describe('safeBatchTransferFrom', function () { + beforeEach(async function () { + await this.token.mint(multiTokenHolder, firstTokenId, firstAmount, '0x', { + from: minter, + }); + await this.token.mint( + multiTokenHolder, + secondTokenId, + secondAmount, + '0x', + { + from: minter, + }, + ); + }); + + it('reverts when transferring amount more than any of balances', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, recipient, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount.addn(1)], + '0x', { from: multiTokenHolder }, + ), + 'ERC1155: insufficient balance for transfer', + ); + }); + + it('reverts when ids array length doesn\'t match amounts array length', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, recipient, + [firstTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ), + 'ERC1155: ids and amounts length mismatch', + ); + + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, recipient, + [firstTokenId, secondTokenId], + [firstAmount], + '0x', { from: multiTokenHolder }, + ), + 'ERC1155: ids and amounts length mismatch', + ); + }); + + it('reverts when transferring to zero address', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, ZERO_ADDRESS, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ), + 'ERC1155: transfer to the zero address', + ); + }); + + function batchTransferWasSuccessful ({ operator, from, ids, values }) { + it('debits transferred balances from sender', async function () { + const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(from), ids); + for (const newBalance of newBalances) { + expect(newBalance).to.be.a.bignumber.equal('0'); + } + }); + + it('credits transferred balances to receiver', async function () { + const newBalances = await this.token.balanceOfBatch(new Array(ids.length).fill(this.toWhom), ids); + for (let i = 0; i < newBalances.length; i++) { + expect(newBalances[i]).to.be.a.bignumber.equal(values[i]); + } + }); + + it('emits a TransferBatch log', function () { + expectEvent(this.transferLogs, 'TransferBatch', { + operator, + from, + to: this.toWhom, + // ids, + // values, + }); + }); + } + + context('when called by the multiTokenHolder', async function () { + beforeEach(async function () { + this.toWhom = recipient; + (this.transferLogs = + await this.token.safeBatchTransferFrom( + multiTokenHolder, recipient, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + )); + }); + + batchTransferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + ids: [firstTokenId, secondTokenId], + values: [firstAmount, secondAmount], + }); + }); + + context('when called by an operator on behalf of the multiTokenHolder', function () { + context('when operator is not approved by multiTokenHolder', function () { + beforeEach(async function () { + await this.token.setApprovalForAll(proxy, false, { from: multiTokenHolder }); + }); + + it('reverts', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, recipient, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: proxy }, + ), + 'ERC1155: caller is not token owner or approved', + ); + }); + }); + + context('when operator is approved by multiTokenHolder', function () { + beforeEach(async function () { + this.toWhom = recipient; + await this.token.setApprovalForAll(proxy, true, { from: multiTokenHolder }); + (this.transferLogs = + await this.token.safeBatchTransferFrom( + multiTokenHolder, recipient, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: proxy }, + )); + }); + + batchTransferWasSuccessful.call(this, { + operator: proxy, + from: multiTokenHolder, + ids: [firstTokenId, secondTokenId], + values: [firstAmount, secondAmount], + }); + + it('preserves operator\'s balances not involved in the transfer', async function () { + const balance1 = await this.token.balanceOf(proxy, firstTokenId); + expect(balance1).to.be.a.bignumber.equal('0'); + const balance2 = await this.token.balanceOf(proxy, secondTokenId); + expect(balance2).to.be.a.bignumber.equal('0'); + }); + }); + }); + + context('when sending to a valid receiver', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + RECEIVER_SINGLE_MAGIC_VALUE, false, + RECEIVER_BATCH_MAGIC_VALUE, false, + ); + }); + + context('without data', function () { + beforeEach(async function () { + this.toWhom = this.receiver.address; + this.transferReceipt = await this.token.safeBatchTransferFrom( + multiTokenHolder, this.receiver.address, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ); + (this.transferLogs = this.transferReceipt); + }); + + batchTransferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + ids: [firstTokenId, secondTokenId], + values: [firstAmount, secondAmount], + }); + + it('calls onERC1155BatchReceived', async function () { + await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', { + operator: multiTokenHolder, + from: multiTokenHolder, + // ids: [firstTokenId, secondTokenId], + // values: [firstAmount, secondAmount], + data: null, + }); + }); + }); + + context('with data', function () { + const data = '0xf00dd00d'; + beforeEach(async function () { + this.toWhom = this.receiver.address; + this.transferReceipt = await this.token.safeBatchTransferFrom( + multiTokenHolder, this.receiver.address, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + data, { from: multiTokenHolder }, + ); + (this.transferLogs = this.transferReceipt); + }); + + batchTransferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + ids: [firstTokenId, secondTokenId], + values: [firstAmount, secondAmount], + }); + + it('calls onERC1155Received', async function () { + await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', { + operator: multiTokenHolder, + from: multiTokenHolder, + // ids: [firstTokenId, secondTokenId], + // values: [firstAmount, secondAmount], + data, + }); + }); + }); + }); + + context('to a receiver contract returning unexpected value', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + RECEIVER_SINGLE_MAGIC_VALUE, false, + RECEIVER_SINGLE_MAGIC_VALUE, false, + ); + }); + + it('reverts', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, this.receiver.address, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ), + 'ERC1155: ERC1155Receiver rejected tokens', + ); + }); + }); + + context('to a receiver contract that reverts', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + RECEIVER_SINGLE_MAGIC_VALUE, false, + RECEIVER_BATCH_MAGIC_VALUE, true, + ); + }); + + it('reverts', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + multiTokenHolder, this.receiver.address, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ), + 'ERC1155ReceiverMock: reverting on batch receive', + ); + }); + }); + + context('to a receiver contract that reverts only on single transfers', function () { + beforeEach(async function () { + this.receiver = await ERC1155ReceiverMock.new( + RECEIVER_SINGLE_MAGIC_VALUE, true, + RECEIVER_BATCH_MAGIC_VALUE, false, + ); + + this.toWhom = this.receiver.address; + this.transferReceipt = await this.token.safeBatchTransferFrom( + multiTokenHolder, this.receiver.address, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ); + (this.transferLogs = this.transferReceipt); + }); + + batchTransferWasSuccessful.call(this, { + operator: multiTokenHolder, + from: multiTokenHolder, + ids: [firstTokenId, secondTokenId], + values: [firstAmount, secondAmount], + }); + + it('calls onERC1155BatchReceived', async function () { + await expectEvent.inTransaction(this.transferReceipt.tx, ERC1155ReceiverMock, 'BatchReceived', { + operator: multiTokenHolder, + from: multiTokenHolder, + // ids: [firstTokenId, secondTokenId], + // values: [firstAmount, secondAmount], + data: null, + }); + }); + }); + + context('to a contract that does not implement the required function', function () { + it('reverts', async function () { + const invalidReceiver = this.token; + await expectRevert.unspecified( + this.token.safeBatchTransferFrom( + multiTokenHolder, invalidReceiver.address, + [firstTokenId, secondTokenId], + [firstAmount, secondAmount], + '0x', { from: multiTokenHolder }, + ), + ); + }); + }); + }); + + shouldSupportInterfaces(['ERC165', 'ERC1155']); + }); +} + +module.exports = { + shouldBehaveLikeERC1155, +}; diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.test.js new file mode 100644 index 0000000..a0a8cf3 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/ERC1155.test.js @@ -0,0 +1,264 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const { shouldBehaveLikeERC1155 } = require('./ERC1155.behavior'); +const ERC1155Mock = artifacts.require('ERC1155Mock'); + +contract('ERC1155', function (accounts) { + const [operator, tokenHolder, tokenBatchHolder, ...otherAccounts] = accounts; + + const initialURI = 'https://token-cdn-domain/{id}.json'; + + beforeEach(async function () { + this.token = await ERC1155Mock.new(initialURI); + }); + + shouldBehaveLikeERC1155(otherAccounts); + + describe('internal functions', function () { + const tokenId = new BN(1990); + const mintAmount = new BN(9001); + const burnAmount = new BN(3000); + + const tokenBatchIds = [new BN(2000), new BN(2010), new BN(2020)]; + const mintAmounts = [new BN(5000), new BN(10000), new BN(42195)]; + const burnAmounts = [new BN(5000), new BN(9001), new BN(195)]; + + const data = '0x12345678'; + + describe('_mint', function () { + it('reverts with a zero destination address', async function () { + await expectRevert( + this.token.mint(ZERO_ADDRESS, tokenId, mintAmount, data), + 'ERC1155: mint to the zero address', + ); + }); + + context('with minted tokens', function () { + beforeEach(async function () { + (this.receipt = await this.token.mint(tokenHolder, tokenId, mintAmount, data, { from: operator })); + }); + + it('emits a TransferSingle event', function () { + expectEvent(this.receipt, 'TransferSingle', { + operator, + from: ZERO_ADDRESS, + to: tokenHolder, + id: tokenId, + value: mintAmount, + }); + }); + + it('credits the minted amount of tokens', async function () { + expect(await this.token.balanceOf(tokenHolder, tokenId)).to.be.bignumber.equal(mintAmount); + }); + }); + }); + + describe('_mintBatch', function () { + it('reverts with a zero destination address', async function () { + await expectRevert( + this.token.mintBatch(ZERO_ADDRESS, tokenBatchIds, mintAmounts, data), + 'ERC1155: mint to the zero address', + ); + }); + + it('reverts if length of inputs do not match', async function () { + await expectRevert( + this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts.slice(1), data), + 'ERC1155: ids and amounts length mismatch', + ); + + await expectRevert( + this.token.mintBatch(tokenBatchHolder, tokenBatchIds.slice(1), mintAmounts, data), + 'ERC1155: ids and amounts length mismatch', + ); + }); + + context('with minted batch of tokens', function () { + beforeEach(async function () { + (this.receipt = await this.token.mintBatch( + tokenBatchHolder, + tokenBatchIds, + mintAmounts, + data, + { from: operator }, + )); + }); + + it('emits a TransferBatch event', function () { + expectEvent(this.receipt, 'TransferBatch', { + operator, + from: ZERO_ADDRESS, + to: tokenBatchHolder, + }); + }); + + it('credits the minted batch of tokens', async function () { + const holderBatchBalances = await this.token.balanceOfBatch( + new Array(tokenBatchIds.length).fill(tokenBatchHolder), + tokenBatchIds, + ); + + for (let i = 0; i < holderBatchBalances.length; i++) { + expect(holderBatchBalances[i]).to.be.bignumber.equal(mintAmounts[i]); + } + }); + }); + }); + + describe('_burn', function () { + it('reverts when burning the zero account\'s tokens', async function () { + await expectRevert( + this.token.burn(ZERO_ADDRESS, tokenId, mintAmount), + 'ERC1155: burn from the zero address', + ); + }); + + it('reverts when burning a non-existent token id', async function () { + await expectRevert( + this.token.burn(tokenHolder, tokenId, mintAmount), + 'ERC1155: burn amount exceeds balance', + ); + }); + + it('reverts when burning more than available tokens', async function () { + await this.token.mint( + tokenHolder, + tokenId, + mintAmount, + data, + { from: operator }, + ); + + await expectRevert( + this.token.burn(tokenHolder, tokenId, mintAmount.addn(1)), + 'ERC1155: burn amount exceeds balance', + ); + }); + + context('with minted-then-burnt tokens', function () { + beforeEach(async function () { + await this.token.mint(tokenHolder, tokenId, mintAmount, data); + (this.receipt = await this.token.burn( + tokenHolder, + tokenId, + burnAmount, + { from: operator }, + )); + }); + + it('emits a TransferSingle event', function () { + expectEvent(this.receipt, 'TransferSingle', { + operator, + from: tokenHolder, + to: ZERO_ADDRESS, + id: tokenId, + value: burnAmount, + }); + }); + + it('accounts for both minting and burning', async function () { + expect(await this.token.balanceOf( + tokenHolder, + tokenId, + )).to.be.bignumber.equal(mintAmount.sub(burnAmount)); + }); + }); + }); + + describe('_burnBatch', function () { + it('reverts when burning the zero account\'s tokens', async function () { + await expectRevert( + this.token.burnBatch(ZERO_ADDRESS, tokenBatchIds, burnAmounts), + 'ERC1155: burn from the zero address', + ); + }); + + it('reverts if length of inputs do not match', async function () { + await expectRevert( + this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts.slice(1)), + 'ERC1155: ids and amounts length mismatch', + ); + + await expectRevert( + this.token.burnBatch(tokenBatchHolder, tokenBatchIds.slice(1), burnAmounts), + 'ERC1155: ids and amounts length mismatch', + ); + }); + + it('reverts when burning a non-existent token id', async function () { + await expectRevert( + this.token.burnBatch(tokenBatchHolder, tokenBatchIds, burnAmounts), + 'ERC1155: burn amount exceeds balance', + ); + }); + + context('with minted-then-burnt tokens', function () { + beforeEach(async function () { + await this.token.mintBatch(tokenBatchHolder, tokenBatchIds, mintAmounts, data); + (this.receipt = await this.token.burnBatch( + tokenBatchHolder, + tokenBatchIds, + burnAmounts, + { from: operator }, + )); + }); + + it('emits a TransferBatch event', function () { + expectEvent(this.receipt, 'TransferBatch', { + operator, + from: tokenBatchHolder, + to: ZERO_ADDRESS, + // ids: tokenBatchIds, + // values: burnAmounts, + }); + }); + + it('accounts for both minting and burning', async function () { + const holderBatchBalances = await this.token.balanceOfBatch( + new Array(tokenBatchIds.length).fill(tokenBatchHolder), + tokenBatchIds, + ); + + for (let i = 0; i < holderBatchBalances.length; i++) { + expect(holderBatchBalances[i]).to.be.bignumber.equal(mintAmounts[i].sub(burnAmounts[i])); + } + }); + }); + }); + }); + + describe('ERC1155MetadataURI', function () { + const firstTokenID = new BN('42'); + const secondTokenID = new BN('1337'); + + it('emits no URI event in constructor', async function () { + await expectEvent.notEmitted.inConstruction(this.token, 'URI'); + }); + + it('sets the initial URI for all token types', async function () { + expect(await this.token.uri(firstTokenID)).to.be.equal(initialURI); + expect(await this.token.uri(secondTokenID)).to.be.equal(initialURI); + }); + + describe('_setURI', function () { + const newURI = 'https://token-cdn-domain/{locale}/{id}.json'; + + it('emits no URI event', async function () { + const receipt = await this.token.setURI(newURI); + + expectEvent.notEmitted(receipt, 'URI'); + }); + + it('sets the new URI for all token types', async function () { + await this.token.setURI(newURI); + + expect(await this.token.uri(firstTokenID)).to.be.equal(newURI); + expect(await this.token.uri(secondTokenID)).to.be.equal(newURI); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Burnable.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Burnable.test.js new file mode 100644 index 0000000..ff6aee0 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Burnable.test.js @@ -0,0 +1,67 @@ +const { BN, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC1155BurnableMock = artifacts.require('ERC1155BurnableMock'); + +contract('ERC1155Burnable', function (accounts) { + const [ holder, operator, other ] = accounts; + + const uri = 'https://token.com'; + + const tokenIds = [new BN('42'), new BN('1137')]; + const amounts = [new BN('3000'), new BN('9902')]; + + beforeEach(async function () { + this.token = await ERC1155BurnableMock.new(uri); + + await this.token.mint(holder, tokenIds[0], amounts[0], '0x'); + await this.token.mint(holder, tokenIds[1], amounts[1], '0x'); + }); + + describe('burn', function () { + it('holder can burn their tokens', async function () { + await this.token.burn(holder, tokenIds[0], amounts[0].subn(1), { from: holder }); + + expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1'); + }); + + it('approved operators can burn the holder\'s tokens', async function () { + await this.token.setApprovalForAll(operator, true, { from: holder }); + await this.token.burn(holder, tokenIds[0], amounts[0].subn(1), { from: operator }); + + expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1'); + }); + + it('unapproved accounts cannot burn the holder\'s tokens', async function () { + await expectRevert( + this.token.burn(holder, tokenIds[0], amounts[0].subn(1), { from: other }), + 'ERC1155: caller is not token owner or approved', + ); + }); + }); + + describe('burnBatch', function () { + it('holder can burn their tokens', async function () { + await this.token.burnBatch(holder, tokenIds, [ amounts[0].subn(1), amounts[1].subn(2) ], { from: holder }); + + expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1'); + expect(await this.token.balanceOf(holder, tokenIds[1])).to.be.bignumber.equal('2'); + }); + + it('approved operators can burn the holder\'s tokens', async function () { + await this.token.setApprovalForAll(operator, true, { from: holder }); + await this.token.burnBatch(holder, tokenIds, [ amounts[0].subn(1), amounts[1].subn(2) ], { from: operator }); + + expect(await this.token.balanceOf(holder, tokenIds[0])).to.be.bignumber.equal('1'); + expect(await this.token.balanceOf(holder, tokenIds[1])).to.be.bignumber.equal('2'); + }); + + it('unapproved accounts cannot burn the holder\'s tokens', async function () { + await expectRevert( + this.token.burnBatch(holder, tokenIds, [ amounts[0].subn(1), amounts[1].subn(2) ], { from: other }), + 'ERC1155: caller is not token owner or approved', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Pausable.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Pausable.test.js new file mode 100644 index 0000000..f7c4052 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Pausable.test.js @@ -0,0 +1,108 @@ +const { BN, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC1155PausableMock = artifacts.require('ERC1155PausableMock'); + +contract('ERC1155Pausable', function (accounts) { + const [ holder, operator, receiver, other ] = accounts; + + const uri = 'https://token.com'; + + beforeEach(async function () { + this.token = await ERC1155PausableMock.new(uri); + }); + + context('when token is paused', function () { + const firstTokenId = new BN('37'); + const firstTokenAmount = new BN('42'); + + const secondTokenId = new BN('19842'); + const secondTokenAmount = new BN('23'); + + beforeEach(async function () { + await this.token.setApprovalForAll(operator, true, { from: holder }); + await this.token.mint(holder, firstTokenId, firstTokenAmount, '0x'); + + await this.token.pause(); + }); + + it('reverts when trying to safeTransferFrom from holder', async function () { + await expectRevert( + this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenAmount, '0x', { from: holder }), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to safeTransferFrom from operator', async function () { + await expectRevert( + this.token.safeTransferFrom(holder, receiver, firstTokenId, firstTokenAmount, '0x', { from: operator }), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to safeBatchTransferFrom from holder', async function () { + await expectRevert( + this.token.safeBatchTransferFrom(holder, receiver, [firstTokenId], [firstTokenAmount], '0x', { from: holder }), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to safeBatchTransferFrom from operator', async function () { + await expectRevert( + this.token.safeBatchTransferFrom( + holder, receiver, [firstTokenId], [firstTokenAmount], '0x', { from: operator }, + ), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to mint', async function () { + await expectRevert( + this.token.mint(holder, secondTokenId, secondTokenAmount, '0x'), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to mintBatch', async function () { + await expectRevert( + this.token.mintBatch(holder, [secondTokenId], [secondTokenAmount], '0x'), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to burn', async function () { + await expectRevert( + this.token.burn(holder, firstTokenId, firstTokenAmount), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to burnBatch', async function () { + await expectRevert( + this.token.burnBatch(holder, [firstTokenId], [firstTokenAmount]), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + describe('setApprovalForAll', function () { + it('approves an operator', async function () { + await this.token.setApprovalForAll(other, true, { from: holder }); + expect(await this.token.isApprovedForAll(holder, other)).to.equal(true); + }); + }); + + describe('balanceOf', function () { + it('returns the amount of tokens owned by the given address', async function () { + const balance = await this.token.balanceOf(holder, firstTokenId); + expect(balance).to.be.bignumber.equal(firstTokenAmount); + }); + }); + + describe('isApprovedForAll', function () { + it('returns the approval of the operator', async function () { + expect(await this.token.isApprovedForAll(holder, operator)).to.equal(true); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Supply.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Supply.test.js new file mode 100644 index 0000000..1a63260 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155Supply.test.js @@ -0,0 +1,111 @@ +const { BN } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC1155SupplyMock = artifacts.require('ERC1155SupplyMock'); + +contract('ERC1155Supply', function (accounts) { + const [ holder ] = accounts; + + const uri = 'https://token.com'; + + const firstTokenId = new BN('37'); + const firstTokenAmount = new BN('42'); + + const secondTokenId = new BN('19842'); + const secondTokenAmount = new BN('23'); + + beforeEach(async function () { + this.token = await ERC1155SupplyMock.new(uri); + }); + + context('before mint', function () { + it('exist', async function () { + expect(await this.token.exists(firstTokenId)).to.be.equal(false); + }); + + it('totalSupply', async function () { + expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal('0'); + }); + }); + + context('after mint', function () { + context('single', function () { + beforeEach(async function () { + await this.token.mint(holder, firstTokenId, firstTokenAmount, '0x'); + }); + + it('exist', async function () { + expect(await this.token.exists(firstTokenId)).to.be.equal(true); + }); + + it('totalSupply', async function () { + expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal(firstTokenAmount); + }); + }); + + context('batch', function () { + beforeEach(async function () { + await this.token.mintBatch( + holder, + [ firstTokenId, secondTokenId ], + [ firstTokenAmount, secondTokenAmount ], + '0x', + ); + }); + + it('exist', async function () { + expect(await this.token.exists(firstTokenId)).to.be.equal(true); + expect(await this.token.exists(secondTokenId)).to.be.equal(true); + }); + + it('totalSupply', async function () { + expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal(firstTokenAmount); + expect(await this.token.totalSupply(secondTokenId)).to.be.bignumber.equal(secondTokenAmount); + }); + }); + }); + + context('after burn', function () { + context('single', function () { + beforeEach(async function () { + await this.token.mint(holder, firstTokenId, firstTokenAmount, '0x'); + await this.token.burn(holder, firstTokenId, firstTokenAmount); + }); + + it('exist', async function () { + expect(await this.token.exists(firstTokenId)).to.be.equal(false); + }); + + it('totalSupply', async function () { + expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal('0'); + }); + }); + + context('batch', function () { + beforeEach(async function () { + await this.token.mintBatch( + holder, + [ firstTokenId, secondTokenId ], + [ firstTokenAmount, secondTokenAmount ], + '0x', + ); + await this.token.burnBatch( + holder, + [ firstTokenId, secondTokenId ], + [ firstTokenAmount, secondTokenAmount ], + ); + }); + + it('exist', async function () { + expect(await this.token.exists(firstTokenId)).to.be.equal(false); + expect(await this.token.exists(secondTokenId)).to.be.equal(false); + }); + + it('totalSupply', async function () { + expect(await this.token.totalSupply(firstTokenId)).to.be.bignumber.equal('0'); + expect(await this.token.totalSupply(secondTokenId)).to.be.bignumber.equal('0'); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155URIStorage.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155URIStorage.test.js new file mode 100644 index 0000000..8f20a4c --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/extensions/ERC1155URIStorage.test.js @@ -0,0 +1,66 @@ +const { BN, expectEvent } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); +const { artifacts } = require('hardhat'); + +const ERC1155URIStorageMock = artifacts.require('ERC1155URIStorageMock'); + +contract(['ERC1155URIStorage'], function (accounts) { + const [ holder ] = accounts; + + const erc1155Uri = 'https://token.com/nfts/'; + const baseUri = 'https://token.com/'; + + const tokenId = new BN('1'); + const amount = new BN('3000'); + + describe('with base uri set', function () { + beforeEach(async function () { + this.token = await ERC1155URIStorageMock.new(erc1155Uri); + await this.token.setBaseURI(baseUri); + + await this.token.mint(holder, tokenId, amount, '0x'); + }); + + it('can request the token uri, returning the erc1155 uri if no token uri was set', async function () { + const receivedTokenUri = await this.token.uri(tokenId); + + expect(receivedTokenUri).to.be.equal(erc1155Uri); + }); + + it('can request the token uri, returning the concatenated uri if a token uri was set', async function () { + const tokenUri = '1234/'; + const receipt = await this.token.setURI(tokenId, tokenUri); + + const receivedTokenUri = await this.token.uri(tokenId); + + const expectedUri = `${baseUri}${tokenUri}`; + expect(receivedTokenUri).to.be.equal(expectedUri); + expectEvent(receipt, 'URI', { value: expectedUri, id: tokenId }); + }); + }); + + describe('with base uri set to the empty string', function () { + beforeEach(async function () { + this.token = await ERC1155URIStorageMock.new(''); + + await this.token.mint(holder, tokenId, amount, '0x'); + }); + + it('can request the token uri, returning an empty string if no token uri was set', async function () { + const receivedTokenUri = await this.token.uri(tokenId); + + expect(receivedTokenUri).to.be.equal(''); + }); + + it('can request the token uri, returning the token uri if a token uri was set', async function () { + const tokenUri = 'ipfs://1234/'; + const receipt = await this.token.setURI(tokenId, tokenUri); + + const receivedTokenUri = await this.token.uri(tokenId); + + expect(receivedTokenUri).to.be.equal(tokenUri); + expectEvent(receipt, 'URI', { value: tokenUri, id: tokenId }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/presets/ERC1155PresetMinterPauser.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/presets/ERC1155PresetMinterPauser.test.js new file mode 100644 index 0000000..a8d83d1 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/presets/ERC1155PresetMinterPauser.test.js @@ -0,0 +1,146 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; +const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); + +const { expect } = require('chai'); + +const ERC1155PresetMinterPauser = artifacts.require('ERC1155PresetMinterPauser'); + +contract('ERC1155PresetMinterPauser', function (accounts) { + const [ deployer, other ] = accounts; + + const firstTokenId = new BN('845'); + const firstTokenIdAmount = new BN('5000'); + + const secondTokenId = new BN('48324'); + const secondTokenIdAmount = new BN('77875'); + + const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const MINTER_ROLE = web3.utils.soliditySha3('MINTER_ROLE'); + const PAUSER_ROLE = web3.utils.soliditySha3('PAUSER_ROLE'); + + const uri = 'https://token.com'; + + beforeEach(async function () { + this.token = await ERC1155PresetMinterPauser.new(uri, { from: deployer }); + }); + + shouldSupportInterfaces(['ERC1155', 'AccessControl', 'AccessControlEnumerable']); + + it('deployer has the default admin role', async function () { + expect(await this.token.getRoleMemberCount(DEFAULT_ADMIN_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(DEFAULT_ADMIN_ROLE, 0)).to.equal(deployer); + }); + + it('deployer has the minter role', async function () { + expect(await this.token.getRoleMemberCount(MINTER_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(MINTER_ROLE, 0)).to.equal(deployer); + }); + + it('deployer has the pauser role', async function () { + expect(await this.token.getRoleMemberCount(PAUSER_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(PAUSER_ROLE, 0)).to.equal(deployer); + }); + + it('minter and pauser role admin is the default admin', async function () { + expect(await this.token.getRoleAdmin(MINTER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + expect(await this.token.getRoleAdmin(PAUSER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + }); + + describe('minting', function () { + it('deployer can mint tokens', async function () { + const receipt = await this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer }); + expectEvent(receipt, 'TransferSingle', + { operator: deployer, from: ZERO_ADDRESS, to: other, value: firstTokenIdAmount, id: firstTokenId }, + ); + + expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal(firstTokenIdAmount); + }); + + it('other accounts cannot mint tokens', async function () { + await expectRevert( + this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: other }), + 'ERC1155PresetMinterPauser: must have minter role to mint', + ); + }); + }); + + describe('batched minting', function () { + it('deployer can batch mint tokens', async function () { + const receipt = await this.token.mintBatch( + other, [firstTokenId, secondTokenId], [firstTokenIdAmount, secondTokenIdAmount], '0x', { from: deployer }, + ); + + expectEvent(receipt, 'TransferBatch', + { operator: deployer, from: ZERO_ADDRESS, to: other }, + ); + + expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal(firstTokenIdAmount); + }); + + it('other accounts cannot batch mint tokens', async function () { + await expectRevert( + this.token.mintBatch( + other, [firstTokenId, secondTokenId], [firstTokenIdAmount, secondTokenIdAmount], '0x', { from: other }, + ), + 'ERC1155PresetMinterPauser: must have minter role to mint', + ); + }); + }); + + describe('pausing', function () { + it('deployer can pause', async function () { + const receipt = await this.token.pause({ from: deployer }); + expectEvent(receipt, 'Paused', { account: deployer }); + + expect(await this.token.paused()).to.equal(true); + }); + + it('deployer can unpause', async function () { + await this.token.pause({ from: deployer }); + + const receipt = await this.token.unpause({ from: deployer }); + expectEvent(receipt, 'Unpaused', { account: deployer }); + + expect(await this.token.paused()).to.equal(false); + }); + + it('cannot mint while paused', async function () { + await this.token.pause({ from: deployer }); + + await expectRevert( + this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer }), + 'ERC1155Pausable: token transfer while paused', + ); + }); + + it('other accounts cannot pause', async function () { + await expectRevert( + this.token.pause({ from: other }), + 'ERC1155PresetMinterPauser: must have pauser role to pause', + ); + }); + + it('other accounts cannot unpause', async function () { + await this.token.pause({ from: deployer }); + + await expectRevert( + this.token.unpause({ from: other }), + 'ERC1155PresetMinterPauser: must have pauser role to unpause', + ); + }); + }); + + describe('burning', function () { + it('holders can burn their tokens', async function () { + await this.token.mint(other, firstTokenId, firstTokenIdAmount, '0x', { from: deployer }); + + const receipt = await this.token.burn(other, firstTokenId, firstTokenIdAmount.subn(1), { from: other }); + expectEvent(receipt, 'TransferSingle', + { operator: other, from: other, to: ZERO_ADDRESS, value: firstTokenIdAmount.subn(1), id: firstTokenId }, + ); + + expect(await this.token.balanceOf(other, firstTokenId)).to.be.bignumber.equal('1'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC1155/utils/ERC1155Holder.test.js b/lib/openzeppelin-contracts/test/token/ERC1155/utils/ERC1155Holder.test.js new file mode 100644 index 0000000..41225c2 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC1155/utils/ERC1155Holder.test.js @@ -0,0 +1,62 @@ +const { BN } = require('@openzeppelin/test-helpers'); + +const ERC1155Holder = artifacts.require('ERC1155Holder'); +const ERC1155Mock = artifacts.require('ERC1155Mock'); + +const { expect } = require('chai'); + +const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); + +contract('ERC1155Holder', function (accounts) { + const [creator] = accounts; + const uri = 'https://token-cdn-domain/{id}.json'; + const multiTokenIds = [new BN(1), new BN(2), new BN(3)]; + const multiTokenAmounts = [new BN(1000), new BN(2000), new BN(3000)]; + const transferData = '0x12345678'; + + beforeEach(async function () { + this.multiToken = await ERC1155Mock.new(uri, { from: creator }); + this.holder = await ERC1155Holder.new(); + await this.multiToken.mintBatch(creator, multiTokenIds, multiTokenAmounts, '0x', { from: creator }); + }); + + shouldSupportInterfaces(['ERC165', 'ERC1155Receiver']); + + it('receives ERC1155 tokens from a single ID', async function () { + await this.multiToken.safeTransferFrom( + creator, + this.holder.address, + multiTokenIds[0], + multiTokenAmounts[0], + transferData, + { from: creator }, + ); + + expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[0])) + .to.be.bignumber.equal(multiTokenAmounts[0]); + + for (let i = 1; i < multiTokenIds.length; i++) { + expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[i])).to.be.bignumber.equal(new BN(0)); + } + }); + + it('receives ERC1155 tokens from a multiple IDs', async function () { + for (let i = 0; i < multiTokenIds.length; i++) { + expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[i])).to.be.bignumber.equal(new BN(0)); + }; + + await this.multiToken.safeBatchTransferFrom( + creator, + this.holder.address, + multiTokenIds, + multiTokenAmounts, + transferData, + { from: creator }, + ); + + for (let i = 0; i < multiTokenIds.length; i++) { + expect(await this.multiToken.balanceOf(this.holder.address, multiTokenIds[i])) + .to.be.bignumber.equal(multiTokenAmounts[i]); + } + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/ERC20.behavior.js b/lib/openzeppelin-contracts/test/token/ERC20/ERC20.behavior.js new file mode 100644 index 0000000..8bc5476 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/ERC20.behavior.js @@ -0,0 +1,333 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { ZERO_ADDRESS, MAX_UINT256 } = constants; + +function shouldBehaveLikeERC20 (errorPrefix, initialSupply, initialHolder, recipient, anotherAccount) { + describe('total supply', function () { + it('returns the total amount of tokens', async function () { + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); + }); + }); + + describe('balanceOf', function () { + describe('when the requested account has no tokens', function () { + it('returns zero', async function () { + expect(await this.token.balanceOf(anotherAccount)).to.be.bignumber.equal('0'); + }); + }); + + describe('when the requested account has some tokens', function () { + it('returns the total amount of tokens', async function () { + expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(initialSupply); + }); + }); + }); + + describe('transfer', function () { + shouldBehaveLikeERC20Transfer(errorPrefix, initialHolder, recipient, initialSupply, + function (from, to, value) { + return this.token.transfer(to, value, { from }); + }, + ); + }); + + describe('transfer from', function () { + const spender = recipient; + + describe('when the token owner is not the zero address', function () { + const tokenOwner = initialHolder; + + describe('when the recipient is not the zero address', function () { + const to = anotherAccount; + + describe('when the spender has enough allowance', function () { + beforeEach(async function () { + await this.token.approve(spender, initialSupply, { from: initialHolder }); + }); + + describe('when the token owner has enough balance', function () { + const amount = initialSupply; + + it('transfers the requested amount', async function () { + await this.token.transferFrom(tokenOwner, to, amount, { from: spender }); + + expect(await this.token.balanceOf(tokenOwner)).to.be.bignumber.equal('0'); + + expect(await this.token.balanceOf(to)).to.be.bignumber.equal(amount); + }); + + it('decreases the spender allowance', async function () { + await this.token.transferFrom(tokenOwner, to, amount, { from: spender }); + + expect(await this.token.allowance(tokenOwner, spender)).to.be.bignumber.equal('0'); + }); + + it('emits a transfer event', async function () { + expectEvent( + await this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + 'Transfer', + { from: tokenOwner, to: to, value: amount }, + ); + }); + + it('emits an approval event', async function () { + expectEvent( + await this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + 'Approval', + { owner: tokenOwner, spender: spender, value: await this.token.allowance(tokenOwner, spender) }, + ); + }); + }); + + describe('when the token owner does not have enough balance', function () { + const amount = initialSupply; + + beforeEach('reducing balance', async function () { + await this.token.transfer(to, 1, { from: tokenOwner }); + }); + + it('reverts', async function () { + await expectRevert( + this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + `${errorPrefix}: transfer amount exceeds balance`, + ); + }); + }); + }); + + describe('when the spender does not have enough allowance', function () { + const allowance = initialSupply.subn(1); + + beforeEach(async function () { + await this.token.approve(spender, allowance, { from: tokenOwner }); + }); + + describe('when the token owner has enough balance', function () { + const amount = initialSupply; + + it('reverts', async function () { + await expectRevert( + this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + `${errorPrefix}: insufficient allowance`, + ); + }); + }); + + describe('when the token owner does not have enough balance', function () { + const amount = allowance; + + beforeEach('reducing balance', async function () { + await this.token.transfer(to, 2, { from: tokenOwner }); + }); + + it('reverts', async function () { + await expectRevert( + this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + `${errorPrefix}: transfer amount exceeds balance`, + ); + }); + }); + }); + + describe('when the spender has unlimited allowance', function () { + beforeEach(async function () { + await this.token.approve(spender, MAX_UINT256, { from: initialHolder }); + }); + + it('does not decrease the spender allowance', async function () { + await this.token.transferFrom(tokenOwner, to, 1, { from: spender }); + + expect(await this.token.allowance(tokenOwner, spender)).to.be.bignumber.equal(MAX_UINT256); + }); + + it('does not emit an approval event', async function () { + expectEvent.notEmitted( + await this.token.transferFrom(tokenOwner, to, 1, { from: spender }), + 'Approval', + ); + }); + }); + }); + + describe('when the recipient is the zero address', function () { + const amount = initialSupply; + const to = ZERO_ADDRESS; + + beforeEach(async function () { + await this.token.approve(spender, amount, { from: tokenOwner }); + }); + + it('reverts', async function () { + await expectRevert(this.token.transferFrom( + tokenOwner, to, amount, { from: spender }), `${errorPrefix}: transfer to the zero address`, + ); + }); + }); + }); + + describe('when the token owner is the zero address', function () { + const amount = 0; + const tokenOwner = ZERO_ADDRESS; + const to = recipient; + + it('reverts', async function () { + await expectRevert( + this.token.transferFrom(tokenOwner, to, amount, { from: spender }), + 'from the zero address', + ); + }); + }); + }); + + describe('approve', function () { + shouldBehaveLikeERC20Approve(errorPrefix, initialHolder, recipient, initialSupply, + function (owner, spender, amount) { + return this.token.approve(spender, amount, { from: owner }); + }, + ); + }); +} + +function shouldBehaveLikeERC20Transfer (errorPrefix, from, to, balance, transfer) { + describe('when the recipient is not the zero address', function () { + describe('when the sender does not have enough balance', function () { + const amount = balance.addn(1); + + it('reverts', async function () { + await expectRevert(transfer.call(this, from, to, amount), + `${errorPrefix}: transfer amount exceeds balance`, + ); + }); + }); + + describe('when the sender transfers all balance', function () { + const amount = balance; + + it('transfers the requested amount', async function () { + await transfer.call(this, from, to, amount); + + expect(await this.token.balanceOf(from)).to.be.bignumber.equal('0'); + + expect(await this.token.balanceOf(to)).to.be.bignumber.equal(amount); + }); + + it('emits a transfer event', async function () { + expectEvent( + await transfer.call(this, from, to, amount), + 'Transfer', + { from, to, value: amount }, + ); + }); + }); + + describe('when the sender transfers zero tokens', function () { + const amount = new BN('0'); + + it('transfers the requested amount', async function () { + await transfer.call(this, from, to, amount); + + expect(await this.token.balanceOf(from)).to.be.bignumber.equal(balance); + + expect(await this.token.balanceOf(to)).to.be.bignumber.equal('0'); + }); + + it('emits a transfer event', async function () { + expectEvent( + await transfer.call(this, from, to, amount), + 'Transfer', + { from, to, value: amount }, + ); + }); + }); + }); + + describe('when the recipient is the zero address', function () { + it('reverts', async function () { + await expectRevert(transfer.call(this, from, ZERO_ADDRESS, balance), + `${errorPrefix}: transfer to the zero address`, + ); + }); + }); +} + +function shouldBehaveLikeERC20Approve (errorPrefix, owner, spender, supply, approve) { + describe('when the spender is not the zero address', function () { + describe('when the sender has enough balance', function () { + const amount = supply; + + it('emits an approval event', async function () { + expectEvent( + await approve.call(this, owner, spender, amount), + 'Approval', + { owner: owner, spender: spender, value: amount }, + ); + }); + + describe('when there was no approved amount before', function () { + it('approves the requested amount', async function () { + await approve.call(this, owner, spender, amount); + + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount); + }); + }); + + describe('when the spender had an approved amount', function () { + beforeEach(async function () { + await approve.call(this, owner, spender, new BN(1)); + }); + + it('approves the requested amount and replaces the previous one', async function () { + await approve.call(this, owner, spender, amount); + + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount); + }); + }); + }); + + describe('when the sender does not have enough balance', function () { + const amount = supply.addn(1); + + it('emits an approval event', async function () { + expectEvent( + await approve.call(this, owner, spender, amount), + 'Approval', + { owner: owner, spender: spender, value: amount }, + ); + }); + + describe('when there was no approved amount before', function () { + it('approves the requested amount', async function () { + await approve.call(this, owner, spender, amount); + + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount); + }); + }); + + describe('when the spender had an approved amount', function () { + beforeEach(async function () { + await approve.call(this, owner, spender, new BN(1)); + }); + + it('approves the requested amount and replaces the previous one', async function () { + await approve.call(this, owner, spender, amount); + + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(amount); + }); + }); + }); + }); + + describe('when the spender is the zero address', function () { + it('reverts', async function () { + await expectRevert(approve.call(this, owner, ZERO_ADDRESS, supply), + `${errorPrefix}: approve to the zero address`, + ); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC20, + shouldBehaveLikeERC20Transfer, + shouldBehaveLikeERC20Approve, +}; diff --git a/lib/openzeppelin-contracts/test/token/ERC20/ERC20.test.js b/lib/openzeppelin-contracts/test/token/ERC20/ERC20.test.js new file mode 100644 index 0000000..992edf9 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/ERC20.test.js @@ -0,0 +1,309 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { ZERO_ADDRESS } = constants; + +const { + shouldBehaveLikeERC20, + shouldBehaveLikeERC20Transfer, + shouldBehaveLikeERC20Approve, +} = require('./ERC20.behavior'); + +const ERC20Mock = artifacts.require('ERC20Mock'); +const ERC20DecimalsMock = artifacts.require('ERC20DecimalsMock'); + +contract('ERC20', function (accounts) { + const [ initialHolder, recipient, anotherAccount ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + + const initialSupply = new BN(100); + + beforeEach(async function () { + this.token = await ERC20Mock.new(name, symbol, initialHolder, initialSupply); + }); + + it('has a name', async function () { + expect(await this.token.name()).to.equal(name); + }); + + it('has a symbol', async function () { + expect(await this.token.symbol()).to.equal(symbol); + }); + + it('has 18 decimals', async function () { + expect(await this.token.decimals()).to.be.bignumber.equal('18'); + }); + + describe('set decimals', function () { + const decimals = new BN(6); + + it('can set decimals during construction', async function () { + const token = await ERC20DecimalsMock.new(name, symbol, decimals); + expect(await token.decimals()).to.be.bignumber.equal(decimals); + }); + }); + + shouldBehaveLikeERC20('ERC20', initialSupply, initialHolder, recipient, anotherAccount); + + describe('decrease allowance', function () { + describe('when the spender is not the zero address', function () { + const spender = recipient; + + function shouldDecreaseApproval (amount) { + describe('when there was no approved amount before', function () { + it('reverts', async function () { + await expectRevert(this.token.decreaseAllowance( + spender, amount, { from: initialHolder }), 'ERC20: decreased allowance below zero', + ); + }); + }); + + describe('when the spender had an approved amount', function () { + const approvedAmount = amount; + + beforeEach(async function () { + await this.token.approve(spender, approvedAmount, { from: initialHolder }); + }); + + it('emits an approval event', async function () { + expectEvent( + await this.token.decreaseAllowance(spender, approvedAmount, { from: initialHolder }), + 'Approval', + { owner: initialHolder, spender: spender, value: new BN(0) }, + ); + }); + + it('decreases the spender allowance subtracting the requested amount', async function () { + await this.token.decreaseAllowance(spender, approvedAmount.subn(1), { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal('1'); + }); + + it('sets the allowance to zero when all allowance is removed', async function () { + await this.token.decreaseAllowance(spender, approvedAmount, { from: initialHolder }); + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal('0'); + }); + + it('reverts when more than the full allowance is removed', async function () { + await expectRevert( + this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }), + 'ERC20: decreased allowance below zero', + ); + }); + }); + } + + describe('when the sender has enough balance', function () { + const amount = initialSupply; + + shouldDecreaseApproval(amount); + }); + + describe('when the sender does not have enough balance', function () { + const amount = initialSupply.addn(1); + + shouldDecreaseApproval(amount); + }); + }); + + describe('when the spender is the zero address', function () { + const amount = initialSupply; + const spender = ZERO_ADDRESS; + + it('reverts', async function () { + await expectRevert(this.token.decreaseAllowance( + spender, amount, { from: initialHolder }), 'ERC20: decreased allowance below zero', + ); + }); + }); + }); + + describe('increase allowance', function () { + const amount = initialSupply; + + describe('when the spender is not the zero address', function () { + const spender = recipient; + + describe('when the sender has enough balance', function () { + it('emits an approval event', async function () { + expectEvent( + await this.token.increaseAllowance(spender, amount, { from: initialHolder }), + 'Approval', + { owner: initialHolder, spender: spender, value: amount }, + ); + }); + + describe('when there was no approved amount before', function () { + it('approves the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount); + }); + }); + + describe('when the spender had an approved amount', function () { + beforeEach(async function () { + await this.token.approve(spender, new BN(1), { from: initialHolder }); + }); + + it('increases the spender allowance adding the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount.addn(1)); + }); + }); + }); + + describe('when the sender does not have enough balance', function () { + const amount = initialSupply.addn(1); + + it('emits an approval event', async function () { + expectEvent( + await this.token.increaseAllowance(spender, amount, { from: initialHolder }), + 'Approval', + { owner: initialHolder, spender: spender, value: amount }, + ); + }); + + describe('when there was no approved amount before', function () { + it('approves the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount); + }); + }); + + describe('when the spender had an approved amount', function () { + beforeEach(async function () { + await this.token.approve(spender, new BN(1), { from: initialHolder }); + }); + + it('increases the spender allowance adding the requested amount', async function () { + await this.token.increaseAllowance(spender, amount, { from: initialHolder }); + + expect(await this.token.allowance(initialHolder, spender)).to.be.bignumber.equal(amount.addn(1)); + }); + }); + }); + }); + + describe('when the spender is the zero address', function () { + const spender = ZERO_ADDRESS; + + it('reverts', async function () { + await expectRevert( + this.token.increaseAllowance(spender, amount, { from: initialHolder }), 'ERC20: approve to the zero address', + ); + }); + }); + }); + + describe('_mint', function () { + const amount = new BN(50); + it('rejects a null account', async function () { + await expectRevert( + this.token.mint(ZERO_ADDRESS, amount), 'ERC20: mint to the zero address', + ); + }); + + describe('for a non zero account', function () { + beforeEach('minting', async function () { + this.receipt = await this.token.mint(recipient, amount); + }); + + it('increments totalSupply', async function () { + const expectedSupply = initialSupply.add(amount); + expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); + }); + + it('increments recipient balance', async function () { + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); + }); + + it('emits Transfer event', async function () { + const event = expectEvent( + this.receipt, + 'Transfer', + { from: ZERO_ADDRESS, to: recipient }, + ); + + expect(event.args.value).to.be.bignumber.equal(amount); + }); + }); + }); + + describe('_burn', function () { + it('rejects a null account', async function () { + await expectRevert(this.token.burn(ZERO_ADDRESS, new BN(1)), + 'ERC20: burn from the zero address'); + }); + + describe('for a non zero account', function () { + it('rejects burning more than balance', async function () { + await expectRevert(this.token.burn( + initialHolder, initialSupply.addn(1)), 'ERC20: burn amount exceeds balance', + ); + }); + + const describeBurn = function (description, amount) { + describe(description, function () { + beforeEach('burning', async function () { + this.receipt = await this.token.burn(initialHolder, amount); + }); + + it('decrements totalSupply', async function () { + const expectedSupply = initialSupply.sub(amount); + expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); + }); + + it('decrements initialHolder balance', async function () { + const expectedBalance = initialSupply.sub(amount); + expect(await this.token.balanceOf(initialHolder)).to.be.bignumber.equal(expectedBalance); + }); + + it('emits Transfer event', async function () { + const event = expectEvent( + this.receipt, + 'Transfer', + { from: initialHolder, to: ZERO_ADDRESS }, + ); + + expect(event.args.value).to.be.bignumber.equal(amount); + }); + }); + }; + + describeBurn('for entire balance', initialSupply); + describeBurn('for less amount than balance', initialSupply.subn(1)); + }); + }); + + describe('_transfer', function () { + shouldBehaveLikeERC20Transfer('ERC20', initialHolder, recipient, initialSupply, function (from, to, amount) { + return this.token.transferInternal(from, to, amount); + }); + + describe('when the sender is the zero address', function () { + it('reverts', async function () { + await expectRevert(this.token.transferInternal(ZERO_ADDRESS, recipient, initialSupply), + 'ERC20: transfer from the zero address', + ); + }); + }); + }); + + describe('_approve', function () { + shouldBehaveLikeERC20Approve('ERC20', initialHolder, recipient, initialSupply, function (owner, spender, amount) { + return this.token.approveInternal(owner, spender, amount); + }); + + describe('when the owner is the zero address', function () { + it('reverts', async function () { + await expectRevert(this.token.approveInternal(ZERO_ADDRESS, recipient, initialSupply), + 'ERC20: approve from the zero address', + ); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.behavior.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.behavior.js new file mode 100644 index 0000000..a931bf6 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.behavior.js @@ -0,0 +1,109 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +function shouldBehaveLikeERC20Burnable (owner, initialBalance, [burner]) { + describe('burn', function () { + describe('when the given amount is not greater than balance of the sender', function () { + context('for a zero amount', function () { + shouldBurn(new BN(0)); + }); + + context('for a non-zero amount', function () { + shouldBurn(new BN(100)); + }); + + function shouldBurn (amount) { + beforeEach(async function () { + (this.receipt = await this.token.burn(amount, { from: owner })); + }); + + it('burns the requested amount', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal(initialBalance.sub(amount)); + }); + + it('emits a transfer event', async function () { + expectEvent(this.receipt, 'Transfer', { + from: owner, + to: ZERO_ADDRESS, + value: amount, + }); + }); + } + }); + + describe('when the given amount is greater than the balance of the sender', function () { + const amount = initialBalance.addn(1); + + it('reverts', async function () { + await expectRevert(this.token.burn(amount, { from: owner }), + 'ERC20: burn amount exceeds balance', + ); + }); + }); + }); + + describe('burnFrom', function () { + describe('on success', function () { + context('for a zero amount', function () { + shouldBurnFrom(new BN(0)); + }); + + context('for a non-zero amount', function () { + shouldBurnFrom(new BN(100)); + }); + + function shouldBurnFrom (amount) { + const originalAllowance = amount.muln(3); + + beforeEach(async function () { + await this.token.approve(burner, originalAllowance, { from: owner }); + this.receipt = await this.token.burnFrom(owner, amount, { from: burner }); + }); + + it('burns the requested amount', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal(initialBalance.sub(amount)); + }); + + it('decrements allowance', async function () { + expect(await this.token.allowance(owner, burner)).to.be.bignumber.equal(originalAllowance.sub(amount)); + }); + + it('emits a transfer event', async function () { + expectEvent(this.receipt, 'Transfer', { + from: owner, + to: ZERO_ADDRESS, + value: amount, + }); + }); + } + }); + + describe('when the given amount is greater than the balance of the sender', function () { + const amount = initialBalance.addn(1); + + it('reverts', async function () { + await this.token.approve(burner, amount, { from: owner }); + await expectRevert(this.token.burnFrom(owner, amount, { from: burner }), + 'ERC20: burn amount exceeds balance', + ); + }); + }); + + describe('when the given amount is greater than the allowance', function () { + const allowance = new BN(100); + + it('reverts', async function () { + await this.token.approve(burner, allowance, { from: owner }); + await expectRevert(this.token.burnFrom(owner, allowance.addn(1), { from: burner }), + 'ERC20: insufficient allowance', + ); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC20Burnable, +}; diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.test.js new file mode 100644 index 0000000..8aa4fb6 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Burnable.test.js @@ -0,0 +1,19 @@ +const { BN } = require('@openzeppelin/test-helpers'); + +const { shouldBehaveLikeERC20Burnable } = require('./ERC20Burnable.behavior'); +const ERC20BurnableMock = artifacts.require('ERC20BurnableMock'); + +contract('ERC20Burnable', function (accounts) { + const [ owner, ...otherAccounts ] = accounts; + + const initialBalance = new BN(1000); + + const name = 'My Token'; + const symbol = 'MTKN'; + + beforeEach(async function () { + this.token = await ERC20BurnableMock.new(name, symbol, owner, initialBalance, { from: owner }); + }); + + shouldBehaveLikeERC20Burnable(owner, initialBalance, otherAccounts); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.behavior.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.behavior.js new file mode 100644 index 0000000..4692f99 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.behavior.js @@ -0,0 +1,32 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +function shouldBehaveLikeERC20Capped (minter, [other], cap) { + describe('capped token', function () { + const from = minter; + + it('starts with the correct cap', async function () { + expect(await this.token.cap()).to.be.bignumber.equal(cap); + }); + + it('mints when amount is less than cap', async function () { + await this.token.mint(other, cap.subn(1), { from }); + expect(await this.token.totalSupply()).to.be.bignumber.equal(cap.subn(1)); + }); + + it('fails to mint if the amount exceeds the cap', async function () { + await this.token.mint(other, cap.subn(1), { from }); + await expectRevert(this.token.mint(other, 2, { from }), 'ERC20Capped: cap exceeded'); + }); + + it('fails to mint after cap is reached', async function () { + await this.token.mint(other, cap, { from }); + await expectRevert(this.token.mint(other, 1, { from }), 'ERC20Capped: cap exceeded'); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC20Capped, +}; diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.test.js new file mode 100644 index 0000000..76532ce --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Capped.test.js @@ -0,0 +1,27 @@ +const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers'); +const { shouldBehaveLikeERC20Capped } = require('./ERC20Capped.behavior'); + +const ERC20Capped = artifacts.require('ERC20CappedMock'); + +contract('ERC20Capped', function (accounts) { + const [ minter, ...otherAccounts ] = accounts; + + const cap = ether('1000'); + + const name = 'My Token'; + const symbol = 'MTKN'; + + it('requires a non-zero cap', async function () { + await expectRevert( + ERC20Capped.new(name, symbol, new BN(0), { from: minter }), 'ERC20Capped: cap is 0', + ); + }); + + context('once deployed', async function () { + beforeEach(async function () { + this.token = await ERC20Capped.new(name, symbol, cap, { from: minter }); + }); + + shouldBehaveLikeERC20Capped(minter, otherAccounts, cap); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20FlashMint.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20FlashMint.test.js new file mode 100644 index 0000000..4354dd9 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20FlashMint.test.js @@ -0,0 +1,144 @@ +/* eslint-disable */ + +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { MAX_UINT256, ZERO_ADDRESS } = constants; + +const ERC20FlashMintMock = artifacts.require('ERC20FlashMintMock'); +const ERC3156FlashBorrowerMock = artifacts.require('ERC3156FlashBorrowerMock'); + +contract('ERC20FlashMint', function (accounts) { + const [initialHolder, other, anotherAccount] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + + const initialSupply = new BN(100); + const loanAmount = new BN(10000000000000); + + beforeEach(async function () { + this.token = await ERC20FlashMintMock.new(name, symbol, initialHolder, initialSupply); + }); + + describe('maxFlashLoan', function () { + it('token match', async function () { + expect(await this.token.maxFlashLoan(this.token.address)).to.be.bignumber.equal(MAX_UINT256.sub(initialSupply)); + }); + + it('token mismatch', async function () { + expect(await this.token.maxFlashLoan(ZERO_ADDRESS)).to.be.bignumber.equal('0'); + }); + }); + + describe('flashFee', function () { + it('token match', async function () { + expect(await this.token.flashFee(this.token.address, loanAmount)).to.be.bignumber.equal('0'); + }); + + it('token mismatch', async function () { + await expectRevert(this.token.flashFee(ZERO_ADDRESS, loanAmount), 'ERC20FlashMint: wrong token'); + }); + }); + + describe('flashFeeReceiver', function () { + it('default receiver', async function () { + expect(await this.token.flashFeeReceiver()).to.be.eq(ZERO_ADDRESS); + }); + }); + + describe('flashLoan', function () { + it('success', async function () { + const receiver = await ERC3156FlashBorrowerMock.new(true, true); + const { tx } = await this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'); + + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, to: receiver.address, value: loanAmount }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: receiver.address, to: ZERO_ADDRESS, value: loanAmount }); + await expectEvent.inTransaction(tx, receiver, 'BalanceOf', { token: this.token.address, account: receiver.address, value: loanAmount }); + await expectEvent.inTransaction(tx, receiver, 'TotalSupply', { token: this.token.address, value: initialSupply.add(loanAmount) }); + + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); + expect(await this.token.balanceOf(receiver.address)).to.be.bignumber.equal('0'); + expect(await this.token.allowance(receiver.address, this.token.address)).to.be.bignumber.equal('0'); + }); + + it('missing return value', async function () { + const receiver = await ERC3156FlashBorrowerMock.new(false, true); + await expectRevert( + this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'), + 'ERC20FlashMint: invalid return value', + ); + }); + + it('missing approval', async function () { + const receiver = await ERC3156FlashBorrowerMock.new(true, false); + await expectRevert( + this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'), + 'ERC20: insufficient allowance', + ); + }); + + it('unavailable funds', async function () { + const receiver = await ERC3156FlashBorrowerMock.new(true, true); + const data = this.token.contract.methods.transfer(other, 10).encodeABI(); + await expectRevert( + this.token.flashLoan(receiver.address, this.token.address, loanAmount, data), + 'ERC20: burn amount exceeds balance', + ); + }); + + it('more than maxFlashLoan', async function () { + const receiver = await ERC3156FlashBorrowerMock.new(true, true); + const data = this.token.contract.methods.transfer(other, 10).encodeABI(); + // _mint overflow reverts using a panic code. No reason string. + await expectRevert.unspecified(this.token.flashLoan(receiver.address, this.token.address, MAX_UINT256, data)); + }); + + describe('custom flash fee & custom fee receiver', function () { + const receiverInitialBalance = new BN(200000); + const flashFee = new BN(5000); + + beforeEach('init receiver balance & set flash fee', async function () { + this.receiver = await ERC3156FlashBorrowerMock.new(true, true); + const receipt = await this.token.mint(this.receiver.address, receiverInitialBalance); + await expectEvent(receipt, 'Transfer', { from: ZERO_ADDRESS, to: this.receiver.address, value: receiverInitialBalance }); + expect(await this.token.balanceOf(this.receiver.address)).to.be.bignumber.equal(receiverInitialBalance); + + await this.token.setFlashFee(flashFee); + expect(await this.token.flashFee(this.token.address, loanAmount)).to.be.bignumber.equal(flashFee); + }); + + it('default flash fee receiver', async function () { + const { tx } = await this.token.flashLoan(this.receiver.address, this.token.address, loanAmount, '0x'); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, to: this.receiver.address, value: loanAmount }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: this.receiver.address, to: ZERO_ADDRESS, value: loanAmount.add(flashFee) }); + await expectEvent.inTransaction(tx, this.receiver, 'BalanceOf', { token: this.token.address, account: this.receiver.address, value: receiverInitialBalance.add(loanAmount) }); + await expectEvent.inTransaction(tx, this.receiver, 'TotalSupply', { token: this.token.address, value: initialSupply.add(receiverInitialBalance).add(loanAmount) }); + + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply.add(receiverInitialBalance).sub(flashFee)); + expect(await this.token.balanceOf(this.receiver.address)).to.be.bignumber.equal(receiverInitialBalance.sub(flashFee)); + expect(await this.token.balanceOf(await this.token.flashFeeReceiver())).to.be.bignumber.equal('0'); + expect(await this.token.allowance(this.receiver.address, this.token.address)).to.be.bignumber.equal('0'); + }); + + it('custom flash fee receiver', async function () { + const flashFeeReceiverAddress = anotherAccount; + await this.token.setFlashFeeReceiver(flashFeeReceiverAddress); + expect(await this.token.flashFeeReceiver()).to.be.eq(flashFeeReceiverAddress); + + expect(await this.token.balanceOf(flashFeeReceiverAddress)).to.be.bignumber.equal('0'); + + const { tx } = await this.token.flashLoan(this.receiver.address, this.token.address, loanAmount, '0x'); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: ZERO_ADDRESS, to: this.receiver.address, value: loanAmount }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: this.receiver.address, to: ZERO_ADDRESS, value: loanAmount }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { from: this.receiver.address, to: flashFeeReceiverAddress, value: flashFee }); + await expectEvent.inTransaction(tx, this.receiver, 'BalanceOf', { token: this.token.address, account: this.receiver.address, value: receiverInitialBalance.add(loanAmount) }); + await expectEvent.inTransaction(tx, this.receiver, 'TotalSupply', { token: this.token.address, value: initialSupply.add(receiverInitialBalance).add(loanAmount) }); + + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply.add(receiverInitialBalance)); + expect(await this.token.balanceOf(this.receiver.address)).to.be.bignumber.equal(receiverInitialBalance.sub(flashFee)); + expect(await this.token.balanceOf(flashFeeReceiverAddress)).to.be.bignumber.equal(flashFee); + expect(await this.token.allowance(this.receiver.address, flashFeeReceiverAddress)).to.be.bignumber.equal('0'); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Pausable.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Pausable.test.js new file mode 100644 index 0000000..8670e2f --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Pausable.test.js @@ -0,0 +1,134 @@ +const { BN, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC20PausableMock = artifacts.require('ERC20PausableMock'); + +contract('ERC20Pausable', function (accounts) { + const [ holder, recipient, anotherAccount ] = accounts; + + const initialSupply = new BN(100); + + const name = 'My Token'; + const symbol = 'MTKN'; + + beforeEach(async function () { + this.token = await ERC20PausableMock.new(name, symbol, holder, initialSupply); + }); + + describe('pausable token', function () { + describe('transfer', function () { + it('allows to transfer when unpaused', async function () { + await this.token.transfer(recipient, initialSupply, { from: holder }); + + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(initialSupply); + }); + + it('allows to transfer when paused and then unpaused', async function () { + await this.token.pause(); + await this.token.unpause(); + + await this.token.transfer(recipient, initialSupply, { from: holder }); + + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(initialSupply); + }); + + it('reverts when trying to transfer when paused', async function () { + await this.token.pause(); + + await expectRevert(this.token.transfer(recipient, initialSupply, { from: holder }), + 'ERC20Pausable: token transfer while paused', + ); + }); + }); + + describe('transfer from', function () { + const allowance = new BN(40); + + beforeEach(async function () { + await this.token.approve(anotherAccount, allowance, { from: holder }); + }); + + it('allows to transfer from when unpaused', async function () { + await this.token.transferFrom(holder, recipient, allowance, { from: anotherAccount }); + + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(allowance); + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(allowance)); + }); + + it('allows to transfer when paused and then unpaused', async function () { + await this.token.pause(); + await this.token.unpause(); + + await this.token.transferFrom(holder, recipient, allowance, { from: anotherAccount }); + + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(allowance); + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(allowance)); + }); + + it('reverts when trying to transfer from when paused', async function () { + await this.token.pause(); + + await expectRevert(this.token.transferFrom( + holder, recipient, allowance, { from: anotherAccount }), 'ERC20Pausable: token transfer while paused', + ); + }); + }); + + describe('mint', function () { + const amount = new BN('42'); + + it('allows to mint when unpaused', async function () { + await this.token.mint(recipient, amount); + + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); + }); + + it('allows to mint when paused and then unpaused', async function () { + await this.token.pause(); + await this.token.unpause(); + + await this.token.mint(recipient, amount); + + expect(await this.token.balanceOf(recipient)).to.be.bignumber.equal(amount); + }); + + it('reverts when trying to mint when paused', async function () { + await this.token.pause(); + + await expectRevert(this.token.mint(recipient, amount), + 'ERC20Pausable: token transfer while paused', + ); + }); + }); + + describe('burn', function () { + const amount = new BN('42'); + + it('allows to burn when unpaused', async function () { + await this.token.burn(holder, amount); + + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(amount)); + }); + + it('allows to burn when paused and then unpaused', async function () { + await this.token.pause(); + await this.token.unpause(); + + await this.token.burn(holder, amount); + + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply.sub(amount)); + }); + + it('reverts when trying to burn when paused', async function () { + await this.token.pause(); + + await expectRevert(this.token.burn(holder, amount), + 'ERC20Pausable: token transfer while paused', + ); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Snapshot.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Snapshot.test.js new file mode 100644 index 0000000..64d9227 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Snapshot.test.js @@ -0,0 +1,204 @@ +const { BN, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const ERC20SnapshotMock = artifacts.require('ERC20SnapshotMock'); + +const { expect } = require('chai'); + +contract('ERC20Snapshot', function (accounts) { + const [ initialHolder, recipient, other ] = accounts; + + const initialSupply = new BN(100); + + const name = 'My Token'; + const symbol = 'MTKN'; + + beforeEach(async function () { + this.token = await ERC20SnapshotMock.new(name, symbol, initialHolder, initialSupply); + }); + + describe('snapshot', function () { + it('emits a snapshot event', async function () { + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot'); + }); + + it('creates increasing snapshots ids, starting from 1', async function () { + for (const id of ['1', '2', '3', '4', '5']) { + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id }); + } + }); + }); + + describe('totalSupplyAt', function () { + it('reverts with a snapshot id of 0', async function () { + await expectRevert(this.token.totalSupplyAt(0), 'ERC20Snapshot: id is 0'); + }); + + it('reverts with a not-yet-created snapshot id', async function () { + await expectRevert(this.token.totalSupplyAt(1), 'ERC20Snapshot: nonexistent id'); + }); + + context('with initial snapshot', function () { + beforeEach(async function () { + this.initialSnapshotId = new BN('1'); + + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id: this.initialSnapshotId }); + }); + + context('with no supply changes after the snapshot', function () { + it('returns the current total supply', async function () { + expect(await this.token.totalSupplyAt(this.initialSnapshotId)).to.be.bignumber.equal(initialSupply); + }); + }); + + context('with supply changes after the snapshot', function () { + beforeEach(async function () { + await this.token.mint(other, new BN('50')); + await this.token.burn(initialHolder, new BN('20')); + }); + + it('returns the total supply before the changes', async function () { + expect(await this.token.totalSupplyAt(this.initialSnapshotId)).to.be.bignumber.equal(initialSupply); + }); + + context('with a second snapshot after supply changes', function () { + beforeEach(async function () { + this.secondSnapshotId = new BN('2'); + + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id: this.secondSnapshotId }); + }); + + it('snapshots return the supply before and after the changes', async function () { + expect(await this.token.totalSupplyAt(this.initialSnapshotId)).to.be.bignumber.equal(initialSupply); + + expect(await this.token.totalSupplyAt(this.secondSnapshotId)).to.be.bignumber.equal( + await this.token.totalSupply(), + ); + }); + }); + + context('with multiple snapshots after supply changes', function () { + beforeEach(async function () { + this.secondSnapshotIds = ['2', '3', '4']; + + for (const id of this.secondSnapshotIds) { + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id }); + } + }); + + it('all posterior snapshots return the supply after the changes', async function () { + expect(await this.token.totalSupplyAt(this.initialSnapshotId)).to.be.bignumber.equal(initialSupply); + + const currentSupply = await this.token.totalSupply(); + + for (const id of this.secondSnapshotIds) { + expect(await this.token.totalSupplyAt(id)).to.be.bignumber.equal(currentSupply); + } + }); + }); + }); + }); + }); + + describe('balanceOfAt', function () { + it('reverts with a snapshot id of 0', async function () { + await expectRevert(this.token.balanceOfAt(other, 0), 'ERC20Snapshot: id is 0'); + }); + + it('reverts with a not-yet-created snapshot id', async function () { + await expectRevert(this.token.balanceOfAt(other, 1), 'ERC20Snapshot: nonexistent id'); + }); + + context('with initial snapshot', function () { + beforeEach(async function () { + this.initialSnapshotId = new BN('1'); + + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id: this.initialSnapshotId }); + }); + + context('with no balance changes after the snapshot', function () { + it('returns the current balance for all accounts', async function () { + expect(await this.token.balanceOfAt(initialHolder, this.initialSnapshotId)) + .to.be.bignumber.equal(initialSupply); + expect(await this.token.balanceOfAt(recipient, this.initialSnapshotId)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOfAt(other, this.initialSnapshotId)).to.be.bignumber.equal('0'); + }); + }); + + context('with balance changes after the snapshot', function () { + beforeEach(async function () { + await this.token.transfer(recipient, new BN('10'), { from: initialHolder }); + await this.token.mint(other, new BN('50')); + await this.token.burn(initialHolder, new BN('20')); + }); + + it('returns the balances before the changes', async function () { + expect(await this.token.balanceOfAt(initialHolder, this.initialSnapshotId)) + .to.be.bignumber.equal(initialSupply); + expect(await this.token.balanceOfAt(recipient, this.initialSnapshotId)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOfAt(other, this.initialSnapshotId)).to.be.bignumber.equal('0'); + }); + + context('with a second snapshot after supply changes', function () { + beforeEach(async function () { + this.secondSnapshotId = new BN('2'); + + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id: this.secondSnapshotId }); + }); + + it('snapshots return the balances before and after the changes', async function () { + expect(await this.token.balanceOfAt(initialHolder, this.initialSnapshotId)) + .to.be.bignumber.equal(initialSupply); + expect(await this.token.balanceOfAt(recipient, this.initialSnapshotId)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOfAt(other, this.initialSnapshotId)).to.be.bignumber.equal('0'); + + expect(await this.token.balanceOfAt(initialHolder, this.secondSnapshotId)).to.be.bignumber.equal( + await this.token.balanceOf(initialHolder), + ); + expect(await this.token.balanceOfAt(recipient, this.secondSnapshotId)).to.be.bignumber.equal( + await this.token.balanceOf(recipient), + ); + expect(await this.token.balanceOfAt(other, this.secondSnapshotId)).to.be.bignumber.equal( + await this.token.balanceOf(other), + ); + }); + }); + + context('with multiple snapshots after supply changes', function () { + beforeEach(async function () { + this.secondSnapshotIds = ['2', '3', '4']; + + for (const id of this.secondSnapshotIds) { + const receipt = await this.token.snapshot(); + expectEvent(receipt, 'Snapshot', { id }); + } + }); + + it('all posterior snapshots return the supply after the changes', async function () { + expect(await this.token.balanceOfAt(initialHolder, this.initialSnapshotId)) + .to.be.bignumber.equal(initialSupply); + expect(await this.token.balanceOfAt(recipient, this.initialSnapshotId)).to.be.bignumber.equal('0'); + expect(await this.token.balanceOfAt(other, this.initialSnapshotId)).to.be.bignumber.equal('0'); + + for (const id of this.secondSnapshotIds) { + expect(await this.token.balanceOfAt(initialHolder, id)).to.be.bignumber.equal( + await this.token.balanceOf(initialHolder), + ); + expect(await this.token.balanceOfAt(recipient, id)).to.be.bignumber.equal( + await this.token.balanceOf(recipient), + ); + expect(await this.token.balanceOfAt(other, id)).to.be.bignumber.equal( + await this.token.balanceOf(other), + ); + } + }); + }); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Votes.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Votes.test.js new file mode 100644 index 0000000..da7b5cd --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Votes.test.js @@ -0,0 +1,518 @@ +/* eslint-disable */ + +const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { MAX_UINT256, ZERO_ADDRESS } = constants; + +const { fromRpcSig } = require('ethereumjs-util'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; + +const ERC20VotesMock = artifacts.require('ERC20VotesMock'); + +const { batchInBlock } = require('../../../helpers/txpool'); +const { EIP712Domain, domainSeparator } = require('../../../helpers/eip712'); + +const Delegation = [ + { name: 'delegatee', type: 'address' }, + { name: 'nonce', type: 'uint256' }, + { name: 'expiry', type: 'uint256' }, +]; + +contract('ERC20Votes', function (accounts) { + const [ holder, recipient, holderDelegatee, other1, other2 ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + const version = '1'; + const supply = new BN('10000000000000000000000000'); + + beforeEach(async function () { + this.token = await ERC20VotesMock.new(name, symbol); + + // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id + // from within the EVM as from the JSON RPC interface. + // See https://github.com/trufflesuite/ganache-core/issues/515 + this.chainId = await this.token.getChainId(); + }); + + it('initial nonce is 0', async function () { + expect(await this.token.nonces(holder)).to.be.bignumber.equal('0'); + }); + + it('domain separator', async function () { + expect( + await this.token.DOMAIN_SEPARATOR(), + ).to.equal( + await domainSeparator(name, version, this.chainId, this.token.address), + ); + }); + + it('minting restriction', async function () { + const amount = new BN('2').pow(new BN('224')); + await expectRevert( + this.token.mint(holder, amount), + 'ERC20Votes: total supply risks overflowing votes', + ); + }); + + it('recent checkpoints', async function () { + await this.token.delegate(holder, { from: holder }); + for (let i = 0; i < 6; i++) { + await this.token.mint(holder, 1); + } + const block = await web3.eth.getBlockNumber(); + expect(await this.token.numCheckpoints(holder)).to.be.bignumber.equal('6'); + // recent + expect(await this.token.getPastVotes(holder, block - 1)).to.be.bignumber.equal('5'); + // non-recent + expect(await this.token.getPastVotes(holder, block - 6)).to.be.bignumber.equal('0'); + }); + + describe('set delegation', function () { + describe('call', function () { + it('delegation with balance', async function () { + await this.token.mint(holder, supply); + expect(await this.token.delegates(holder)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.token.delegate(holder, { from: holder }); + expectEvent(receipt, 'DelegateChanged', { + delegator: holder, + fromDelegate: ZERO_ADDRESS, + toDelegate: holder, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: holder, + previousBalance: '0', + newBalance: supply, + }); + + expect(await this.token.delegates(holder)).to.be.equal(holder); + + expect(await this.token.getVotes(holder)).to.be.bignumber.equal(supply); + expect(await this.token.getPastVotes(holder, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.token.getPastVotes(holder, receipt.blockNumber)).to.be.bignumber.equal(supply); + }); + + it('delegation without balance', async function () { + expect(await this.token.delegates(holder)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.token.delegate(holder, { from: holder }); + expectEvent(receipt, 'DelegateChanged', { + delegator: holder, + fromDelegate: ZERO_ADDRESS, + toDelegate: holder, + }); + expectEvent.notEmitted(receipt, 'DelegateVotesChanged'); + + expect(await this.token.delegates(holder)).to.be.equal(holder); + }); + }); + + describe('with signature', function () { + const delegator = Wallet.generate(); + const delegatorAddress = web3.utils.toChecksumAddress(delegator.getAddressString()); + const nonce = 0; + + const buildData = (chainId, verifyingContract, message) => ({ data: { + primaryType: 'Delegation', + types: { EIP712Domain, Delegation }, + domain: { name, version, chainId, verifyingContract }, + message, + }}); + + beforeEach(async function () { + await this.token.mint(delegatorAddress, supply); + }); + + it('accept signed delegation', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + expect(await this.token.delegates(delegatorAddress)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.token.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s); + expectEvent(receipt, 'DelegateChanged', { + delegator: delegatorAddress, + fromDelegate: ZERO_ADDRESS, + toDelegate: delegatorAddress, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: delegatorAddress, + previousBalance: '0', + newBalance: supply, + }); + + expect(await this.token.delegates(delegatorAddress)).to.be.equal(delegatorAddress); + + expect(await this.token.getVotes(delegatorAddress)).to.be.bignumber.equal(supply); + expect(await this.token.getPastVotes(delegatorAddress, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.token.getPastVotes(delegatorAddress, receipt.blockNumber)).to.be.bignumber.equal(supply); + }); + + it('rejects reused signature', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + await this.token.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s); + + await expectRevert( + this.token.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s), + 'ERC20Votes: invalid nonce', + ); + }); + + it('rejects bad delegatee', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + const receipt = await this.token.delegateBySig(holderDelegatee, nonce, MAX_UINT256, v, r, s); + const { args } = receipt.logs.find(({ event }) => event == 'DelegateChanged'); + expect(args.delegator).to.not.be.equal(delegatorAddress); + expect(args.fromDelegate).to.be.equal(ZERO_ADDRESS); + expect(args.toDelegate).to.be.equal(holderDelegatee); + }); + + it('rejects bad nonce', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + await expectRevert( + this.token.delegateBySig(delegatorAddress, nonce + 1, MAX_UINT256, v, r, s), + 'ERC20Votes: invalid nonce', + ); + }); + + it('rejects expired permit', async function () { + const expiry = (await time.latest()) - time.duration.weeks(1); + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry, + }), + )); + + await expectRevert( + this.token.delegateBySig(delegatorAddress, nonce, expiry, v, r, s), + 'ERC20Votes: signature expired', + ); + }); + }); + }); + + describe('change delegation', function () { + beforeEach(async function () { + await this.token.mint(holder, supply); + await this.token.delegate(holder, { from: holder }); + }); + + it('call', async function () { + expect(await this.token.delegates(holder)).to.be.equal(holder); + + const { receipt } = await this.token.delegate(holderDelegatee, { from: holder }); + expectEvent(receipt, 'DelegateChanged', { + delegator: holder, + fromDelegate: holder, + toDelegate: holderDelegatee, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: holder, + previousBalance: supply, + newBalance: '0', + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: holderDelegatee, + previousBalance: '0', + newBalance: supply, + }); + + expect(await this.token.delegates(holder)).to.be.equal(holderDelegatee); + + expect(await this.token.getVotes(holder)).to.be.bignumber.equal('0'); + expect(await this.token.getVotes(holderDelegatee)).to.be.bignumber.equal(supply); + expect(await this.token.getPastVotes(holder, receipt.blockNumber - 1)).to.be.bignumber.equal(supply); + expect(await this.token.getPastVotes(holderDelegatee, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.token.getPastVotes(holder, receipt.blockNumber)).to.be.bignumber.equal('0'); + expect(await this.token.getPastVotes(holderDelegatee, receipt.blockNumber)).to.be.bignumber.equal(supply); + }); + }); + + describe('transfers', function () { + beforeEach(async function () { + await this.token.mint(holder, supply); + }); + + it('no delegation', async function () { + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent.notEmitted(receipt, 'DelegateVotesChanged'); + + this.holderVotes = '0'; + this.recipientVotes = '0'; + }); + + it('sender delegation', async function () { + await this.token.delegate(holder, { from: holder }); + + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: holder, previousBalance: supply, newBalance: supply.subn(1) }); + + const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer'); + expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true); + + this.holderVotes = supply.subn(1); + this.recipientVotes = '0'; + }); + + it('receiver delegation', async function () { + await this.token.delegate(recipient, { from: recipient }); + + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: recipient, previousBalance: '0', newBalance: '1' }); + + const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer'); + expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true); + + this.holderVotes = '0'; + this.recipientVotes = '1'; + }); + + it('full delegation', async function () { + await this.token.delegate(holder, { from: holder }); + await this.token.delegate(recipient, { from: recipient }); + + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: holder, previousBalance: supply, newBalance: supply.subn(1) }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: recipient, previousBalance: '0', newBalance: '1' }); + + const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer'); + expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true); + + this.holderVotes = supply.subn(1); + this.recipientVotes = '1'; + }); + + afterEach(async function () { + expect(await this.token.getVotes(holder)).to.be.bignumber.equal(this.holderVotes); + expect(await this.token.getVotes(recipient)).to.be.bignumber.equal(this.recipientVotes); + + // need to advance 2 blocks to see the effect of a transfer on "getPastVotes" + const blockNumber = await time.latestBlock(); + await time.advanceBlock(); + expect(await this.token.getPastVotes(holder, blockNumber)).to.be.bignumber.equal(this.holderVotes); + expect(await this.token.getPastVotes(recipient, blockNumber)).to.be.bignumber.equal(this.recipientVotes); + }); + }); + + // The following tests are a adaptation of https://github.com/compound-finance/compound-protocol/blob/master/tests/Governance/CompTest.js. + describe('Compound test suite', function () { + beforeEach(async function () { + await this.token.mint(holder, supply); + }); + + describe('balanceOf', function () { + it('grants to initial account', async function () { + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('10000000000000000000000000'); + }); + }); + + describe('numCheckpoints', function () { + it('returns the number of checkpoints for a delegate', async function () { + await this.token.transfer(recipient, '100', { from: holder }); //give an account a few tokens for readability + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('0'); + + const t1 = await this.token.delegate(other1, { from: recipient }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('1'); + + const t2 = await this.token.transfer(other2, 10, { from: recipient }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('2'); + + const t3 = await this.token.transfer(other2, 10, { from: recipient }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('3'); + + const t4 = await this.token.transfer(recipient, 20, { from: holder }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('4'); + + expect(await this.token.checkpoints(other1, 0)).to.be.deep.equal([ t1.receipt.blockNumber.toString(), '100' ]); + expect(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ t2.receipt.blockNumber.toString(), '90' ]); + expect(await this.token.checkpoints(other1, 2)).to.be.deep.equal([ t3.receipt.blockNumber.toString(), '80' ]); + expect(await this.token.checkpoints(other1, 3)).to.be.deep.equal([ t4.receipt.blockNumber.toString(), '100' ]); + + await time.advanceBlock(); + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal('100'); + expect(await this.token.getPastVotes(other1, t2.receipt.blockNumber)).to.be.bignumber.equal('90'); + expect(await this.token.getPastVotes(other1, t3.receipt.blockNumber)).to.be.bignumber.equal('80'); + expect(await this.token.getPastVotes(other1, t4.receipt.blockNumber)).to.be.bignumber.equal('100'); + }); + + it('does not add more than one checkpoint in a block', async function () { + await this.token.transfer(recipient, '100', { from: holder }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('0'); + + const [ t1, t2, t3 ] = await batchInBlock([ + () => this.token.delegate(other1, { from: recipient, gas: 100000 }), + () => this.token.transfer(other2, 10, { from: recipient, gas: 100000 }), + () => this.token.transfer(other2, 10, { from: recipient, gas: 100000 }), + ]); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('1'); + expect(await this.token.checkpoints(other1, 0)).to.be.deep.equal([ t1.receipt.blockNumber.toString(), '80' ]); + // expectReve(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ '0', '0' ]); // Reverts due to array overflow check + // expect(await this.token.checkpoints(other1, 2)).to.be.deep.equal([ '0', '0' ]); // Reverts due to array overflow check + + const t4 = await this.token.transfer(recipient, 20, { from: holder }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('2'); + expect(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ t4.receipt.blockNumber.toString(), '100' ]); + }); + }); + + describe('getPastVotes', function () { + it('reverts if block number >= current block', async function () { + await expectRevert( + this.token.getPastVotes(other1, 5e10), + 'ERC20Votes: block not yet mined', + ); + }); + + it('returns 0 if there are no checkpoints', async function () { + expect(await this.token.getPastVotes(other1, 0)).to.be.bignumber.equal('0'); + }); + + it('returns the latest block if >= last checkpoint block', async function () { + const t1 = await this.token.delegate(other1, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + + it('returns zero if < first checkpoint block', async function () { + await time.advanceBlock(); + const t1 = await this.token.delegate(other1, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + + it('generally returns the voting balance at the appropriate checkpoint', async function () { + const t1 = await this.token.delegate(other1, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + const t2 = await this.token.transfer(other2, 10, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + const t3 = await this.token.transfer(other2, 10, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + const t4 = await this.token.transfer(holder, 20, { from: other2 }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastVotes(other1, t2.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPastVotes(other1, t2.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPastVotes(other1, t3.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPastVotes(other1, t3.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPastVotes(other1, t4.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastVotes(other1, t4.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + }); + }); + + describe('getPastTotalSupply', function () { + beforeEach(async function () { + await this.token.delegate(holder, { from: holder }); + }); + + it('reverts if block number >= current block', async function () { + await expectRevert( + this.token.getPastTotalSupply(5e10), + 'ERC20Votes: block not yet mined', + ); + }); + + it('returns 0 if there are no checkpoints', async function () { + expect(await this.token.getPastTotalSupply(0)).to.be.bignumber.equal('0'); + }); + + it('returns the latest block if >= last checkpoint block', async function () { + t1 = await this.token.mint(holder, supply); + + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal(supply); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal(supply); + }); + + it('returns zero if < first checkpoint block', async function () { + await time.advanceBlock(); + const t1 = await this.token.mint(holder, supply); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + + it('generally returns the voting balance at the appropriate checkpoint', async function () { + const t1 = await this.token.mint(holder, supply); + await time.advanceBlock(); + await time.advanceBlock(); + const t2 = await this.token.burn(holder, 10); + await time.advanceBlock(); + await time.advanceBlock(); + const t3 = await this.token.burn(holder, 10); + await time.advanceBlock(); + await time.advanceBlock(); + const t4 = await this.token.mint(holder, 20); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastTotalSupply(t2.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPastTotalSupply(t3.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPastTotalSupply(t3.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPastTotalSupply(t4.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastTotalSupply(t4.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20VotesComp.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20VotesComp.test.js new file mode 100644 index 0000000..d030509 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20VotesComp.test.js @@ -0,0 +1,496 @@ +/* eslint-disable */ + +const { BN, constants, expectEvent, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { MAX_UINT256, ZERO_ADDRESS } = constants; + +const { fromRpcSig } = require('ethereumjs-util'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; + +const ERC20VotesCompMock = artifacts.require('ERC20VotesCompMock'); + +const { batchInBlock } = require('../../../helpers/txpool'); +const { EIP712Domain, domainSeparator } = require('../../../helpers/eip712'); + +const Delegation = [ + { name: 'delegatee', type: 'address' }, + { name: 'nonce', type: 'uint256' }, + { name: 'expiry', type: 'uint256' }, +]; + +contract('ERC20VotesComp', function (accounts) { + const [ holder, recipient, holderDelegatee, other1, other2 ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + const version = '1'; + const supply = new BN('10000000000000000000000000'); + + beforeEach(async function () { + this.token = await ERC20VotesCompMock.new(name, symbol); + + // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id + // from within the EVM as from the JSON RPC interface. + // See https://github.com/trufflesuite/ganache-core/issues/515 + this.chainId = await this.token.getChainId(); + }); + + it('initial nonce is 0', async function () { + expect(await this.token.nonces(holder)).to.be.bignumber.equal('0'); + }); + + it('domain separator', async function () { + expect( + await this.token.DOMAIN_SEPARATOR(), + ).to.equal( + await domainSeparator(name, version, this.chainId, this.token.address), + ); + }); + + it('minting restriction', async function () { + const amount = new BN('2').pow(new BN('96')); + await expectRevert( + this.token.mint(holder, amount), + 'ERC20Votes: total supply risks overflowing votes', + ); + }); + + describe('set delegation', function () { + describe('call', function () { + it('delegation with balance', async function () { + await this.token.mint(holder, supply); + expect(await this.token.delegates(holder)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.token.delegate(holder, { from: holder }); + expectEvent(receipt, 'DelegateChanged', { + delegator: holder, + fromDelegate: ZERO_ADDRESS, + toDelegate: holder, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: holder, + previousBalance: '0', + newBalance: supply, + }); + + expect(await this.token.delegates(holder)).to.be.equal(holder); + + expect(await this.token.getCurrentVotes(holder)).to.be.bignumber.equal(supply); + expect(await this.token.getPriorVotes(holder, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.token.getPriorVotes(holder, receipt.blockNumber)).to.be.bignumber.equal(supply); + }); + + it('delegation without balance', async function () { + expect(await this.token.delegates(holder)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.token.delegate(holder, { from: holder }); + expectEvent(receipt, 'DelegateChanged', { + delegator: holder, + fromDelegate: ZERO_ADDRESS, + toDelegate: holder, + }); + expectEvent.notEmitted(receipt, 'DelegateVotesChanged'); + + expect(await this.token.delegates(holder)).to.be.equal(holder); + }); + }); + + describe('with signature', function () { + const delegator = Wallet.generate(); + const delegatorAddress = web3.utils.toChecksumAddress(delegator.getAddressString()); + const nonce = 0; + + const buildData = (chainId, verifyingContract, message) => ({ data: { + primaryType: 'Delegation', + types: { EIP712Domain, Delegation }, + domain: { name, version, chainId, verifyingContract }, + message, + }}); + + beforeEach(async function () { + await this.token.mint(delegatorAddress, supply); + }); + + it('accept signed delegation', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + expect(await this.token.delegates(delegatorAddress)).to.be.equal(ZERO_ADDRESS); + + const { receipt } = await this.token.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s); + expectEvent(receipt, 'DelegateChanged', { + delegator: delegatorAddress, + fromDelegate: ZERO_ADDRESS, + toDelegate: delegatorAddress, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: delegatorAddress, + previousBalance: '0', + newBalance: supply, + }); + + expect(await this.token.delegates(delegatorAddress)).to.be.equal(delegatorAddress); + + expect(await this.token.getCurrentVotes(delegatorAddress)).to.be.bignumber.equal(supply); + expect(await this.token.getPriorVotes(delegatorAddress, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.token.getPriorVotes(delegatorAddress, receipt.blockNumber)).to.be.bignumber.equal(supply); + }); + + it('rejects reused signature', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + await this.token.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s); + + await expectRevert( + this.token.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s), + 'ERC20Votes: invalid nonce', + ); + }); + + it('rejects bad delegatee', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + + const receipt = await this.token.delegateBySig(holderDelegatee, nonce, MAX_UINT256, v, r, s); + const { args } = receipt.logs.find(({ event }) => event == 'DelegateChanged'); + expect(args.delegator).to.not.be.equal(delegatorAddress); + expect(args.fromDelegate).to.be.equal(ZERO_ADDRESS); + expect(args.toDelegate).to.be.equal(holderDelegatee); + }); + + it('rejects bad nonce', async function () { + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry: MAX_UINT256, + }), + )); + await expectRevert( + this.token.delegateBySig(delegatorAddress, nonce + 1, MAX_UINT256, v, r, s), + 'ERC20Votes: invalid nonce', + ); + }); + + it('rejects expired permit', async function () { + const expiry = (await time.latest()) - time.duration.weeks(1); + const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage( + delegator.getPrivateKey(), + buildData(this.chainId, this.token.address, { + delegatee: delegatorAddress, + nonce, + expiry, + }), + )); + + await expectRevert( + this.token.delegateBySig(delegatorAddress, nonce, expiry, v, r, s), + 'ERC20Votes: signature expired', + ); + }); + }); + }); + + describe('change delegation', function () { + beforeEach(async function () { + await this.token.mint(holder, supply); + await this.token.delegate(holder, { from: holder }); + }); + + it('call', async function () { + expect(await this.token.delegates(holder)).to.be.equal(holder); + + const { receipt } = await this.token.delegate(holderDelegatee, { from: holder }); + expectEvent(receipt, 'DelegateChanged', { + delegator: holder, + fromDelegate: holder, + toDelegate: holderDelegatee, + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: holder, + previousBalance: supply, + newBalance: '0', + }); + expectEvent(receipt, 'DelegateVotesChanged', { + delegate: holderDelegatee, + previousBalance: '0', + newBalance: supply, + }); + + expect(await this.token.delegates(holder)).to.be.equal(holderDelegatee); + + expect(await this.token.getCurrentVotes(holder)).to.be.bignumber.equal('0'); + expect(await this.token.getCurrentVotes(holderDelegatee)).to.be.bignumber.equal(supply); + expect(await this.token.getPriorVotes(holder, receipt.blockNumber - 1)).to.be.bignumber.equal(supply); + expect(await this.token.getPriorVotes(holderDelegatee, receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + await time.advanceBlock(); + expect(await this.token.getPriorVotes(holder, receipt.blockNumber)).to.be.bignumber.equal('0'); + expect(await this.token.getPriorVotes(holderDelegatee, receipt.blockNumber)).to.be.bignumber.equal(supply); + }); + }); + + describe('transfers', function () { + beforeEach(async function () { + await this.token.mint(holder, supply); + }); + + it('no delegation', async function () { + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent.notEmitted(receipt, 'DelegateVotesChanged'); + + this.holderVotes = '0'; + this.recipientVotes = '0'; + }); + + it('sender delegation', async function () { + await this.token.delegate(holder, { from: holder }); + + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: holder, previousBalance: supply, newBalance: supply.subn(1) }); + + this.holderVotes = supply.subn(1); + this.recipientVotes = '0'; + }); + + it('receiver delegation', async function () { + await this.token.delegate(recipient, { from: recipient }); + + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: recipient, previousBalance: '0', newBalance: '1' }); + + this.holderVotes = '0'; + this.recipientVotes = '1'; + }); + + it('full delegation', async function () { + await this.token.delegate(holder, { from: holder }); + await this.token.delegate(recipient, { from: recipient }); + + const { receipt } = await this.token.transfer(recipient, 1, { from: holder }); + expectEvent(receipt, 'Transfer', { from: holder, to: recipient, value: '1' }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: holder, previousBalance: supply, newBalance: supply.subn(1) }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: recipient, previousBalance: '0', newBalance: '1' }); + + this.holderVotes = supply.subn(1); + this.recipientVotes = '1'; + }); + + afterEach(async function () { + expect(await this.token.getCurrentVotes(holder)).to.be.bignumber.equal(this.holderVotes); + expect(await this.token.getCurrentVotes(recipient)).to.be.bignumber.equal(this.recipientVotes); + + // need to advance 2 blocks to see the effect of a transfer on "getPriorVotes" + const blockNumber = await time.latestBlock(); + await time.advanceBlock(); + expect(await this.token.getPriorVotes(holder, blockNumber)).to.be.bignumber.equal(this.holderVotes); + expect(await this.token.getPriorVotes(recipient, blockNumber)).to.be.bignumber.equal(this.recipientVotes); + }); + }); + + // The following tests are a adaptation of https://github.com/compound-finance/compound-protocol/blob/master/tests/Governance/CompTest.js. + describe('Compound test suite', function () { + beforeEach(async function () { + await this.token.mint(holder, supply); + }); + + describe('balanceOf', function () { + it('grants to initial account', async function () { + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('10000000000000000000000000'); + }); + }); + + describe('numCheckpoints', function () { + it('returns the number of checkpoints for a delegate', async function () { + await this.token.transfer(recipient, '100', { from: holder }); //give an account a few tokens for readability + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('0'); + + const t1 = await this.token.delegate(other1, { from: recipient }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('1'); + + const t2 = await this.token.transfer(other2, 10, { from: recipient }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('2'); + + const t3 = await this.token.transfer(other2, 10, { from: recipient }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('3'); + + const t4 = await this.token.transfer(recipient, 20, { from: holder }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('4'); + + expect(await this.token.checkpoints(other1, 0)).to.be.deep.equal([ t1.receipt.blockNumber.toString(), '100' ]); + expect(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ t2.receipt.blockNumber.toString(), '90' ]); + expect(await this.token.checkpoints(other1, 2)).to.be.deep.equal([ t3.receipt.blockNumber.toString(), '80' ]); + expect(await this.token.checkpoints(other1, 3)).to.be.deep.equal([ t4.receipt.blockNumber.toString(), '100' ]); + + await time.advanceBlock(); + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal('100'); + expect(await this.token.getPriorVotes(other1, t2.receipt.blockNumber)).to.be.bignumber.equal('90'); + expect(await this.token.getPriorVotes(other1, t3.receipt.blockNumber)).to.be.bignumber.equal('80'); + expect(await this.token.getPriorVotes(other1, t4.receipt.blockNumber)).to.be.bignumber.equal('100'); + }); + + it('does not add more than one checkpoint in a block', async function () { + await this.token.transfer(recipient, '100', { from: holder }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('0'); + + const [ t1, t2, t3 ] = await batchInBlock([ + () => this.token.delegate(other1, { from: recipient, gas: 100000 }), + () => this.token.transfer(other2, 10, { from: recipient, gas: 100000 }), + () => this.token.transfer(other2, 10, { from: recipient, gas: 100000 }), + ]); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('1'); + expect(await this.token.checkpoints(other1, 0)).to.be.deep.equal([ t1.receipt.blockNumber.toString(), '80' ]); + // expectReve(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ '0', '0' ]); // Reverts due to array overflow check + // expect(await this.token.checkpoints(other1, 2)).to.be.deep.equal([ '0', '0' ]); // Reverts due to array overflow check + + const t4 = await this.token.transfer(recipient, 20, { from: holder }); + expect(await this.token.numCheckpoints(other1)).to.be.bignumber.equal('2'); + expect(await this.token.checkpoints(other1, 1)).to.be.deep.equal([ t4.receipt.blockNumber.toString(), '100' ]); + }); + }); + + describe('getPriorVotes', function () { + it('reverts if block number >= current block', async function () { + await expectRevert( + this.token.getPriorVotes(other1, 5e10), + 'ERC20Votes: block not yet mined', + ); + }); + + it('returns 0 if there are no checkpoints', async function () { + expect(await this.token.getPriorVotes(other1, 0)).to.be.bignumber.equal('0'); + }); + + it('returns the latest block if >= last checkpoint block', async function () { + const t1 = await this.token.delegate(other1, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + + it('returns zero if < first checkpoint block', async function () { + await time.advanceBlock(); + const t1 = await this.token.delegate(other1, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + + it('generally returns the voting balance at the appropriate checkpoint', async function () { + const t1 = await this.token.delegate(other1, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + const t2 = await this.token.transfer(other2, 10, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + const t3 = await this.token.transfer(other2, 10, { from: holder }); + await time.advanceBlock(); + await time.advanceBlock(); + const t4 = await this.token.transfer(holder, 20, { from: other2 }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPriorVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPriorVotes(other1, t2.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPriorVotes(other1, t2.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPriorVotes(other1, t3.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPriorVotes(other1, t3.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPriorVotes(other1, t4.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPriorVotes(other1, t4.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + }); + }); + + describe('getPastTotalSupply', function () { + beforeEach(async function () { + await this.token.delegate(holder, { from: holder }); + }); + + it('reverts if block number >= current block', async function () { + await expectRevert( + this.token.getPastTotalSupply(5e10), + 'ERC20Votes: block not yet mined', + ); + }); + + it('returns 0 if there are no checkpoints', async function () { + expect(await this.token.getPastTotalSupply(0)).to.be.bignumber.equal('0'); + }); + + it('returns the latest block if >= last checkpoint block', async function () { + t1 = await this.token.mint(holder, supply); + + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal(supply); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal(supply); + }); + + it('returns zero if < first checkpoint block', async function () { + await time.advanceBlock(); + const t1 = await this.token.mint(holder, supply); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + + it('generally returns the voting balance at the appropriate checkpoint', async function () { + const t1 = await this.token.mint(holder, supply); + await time.advanceBlock(); + await time.advanceBlock(); + const t2 = await this.token.burn(holder, 10); + await time.advanceBlock(); + await time.advanceBlock(); + const t3 = await this.token.burn(holder, 10); + await time.advanceBlock(); + await time.advanceBlock(); + const t4 = await this.token.mint(holder, 20); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastTotalSupply(t2.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999990'); + expect(await this.token.getPastTotalSupply(t3.receipt.blockNumber)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPastTotalSupply(t3.receipt.blockNumber + 1)).to.be.bignumber.equal('9999999999999999999999980'); + expect(await this.token.getPastTotalSupply(t4.receipt.blockNumber)).to.be.bignumber.equal('10000000000000000000000000'); + expect(await this.token.getPastTotalSupply(t4.receipt.blockNumber + 1)).to.be.bignumber.equal('10000000000000000000000000'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Wrapper.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Wrapper.test.js new file mode 100644 index 0000000..ceb813e --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC20Wrapper.test.js @@ -0,0 +1,190 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { ZERO_ADDRESS, MAX_UINT256 } = constants; + +const { shouldBehaveLikeERC20 } = require('../ERC20.behavior'); + +const NotAnERC20 = artifacts.require('CallReceiverMock'); +const ERC20Mock = artifacts.require('ERC20DecimalsMock'); +const ERC20WrapperMock = artifacts.require('ERC20WrapperMock'); + +contract('ERC20', function (accounts) { + const [ initialHolder, recipient, anotherAccount ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + + const initialSupply = new BN(100); + + beforeEach(async function () { + this.underlying = await ERC20Mock.new(name, symbol, 9); + this.token = await ERC20WrapperMock.new(this.underlying.address, `Wrapped ${name}`, `W${symbol}`); + + await this.underlying.mint(initialHolder, initialSupply); + }); + + afterEach(async function () { + expect(await this.underlying.balanceOf(this.token.address)).to.be.bignumber.equal(await this.token.totalSupply()); + }); + + it('has a name', async function () { + expect(await this.token.name()).to.equal(`Wrapped ${name}`); + }); + + it('has a symbol', async function () { + expect(await this.token.symbol()).to.equal(`W${symbol}`); + }); + + it('has the same decimals as the underlying token', async function () { + expect(await this.token.decimals()).to.be.bignumber.equal('9'); + }); + + it('decimals default back to 18 if token has no metadata', async function () { + const noDecimals = await NotAnERC20.new(); + const otherToken = await ERC20WrapperMock.new(noDecimals.address, `Wrapped ${name}`, `W${symbol}`); + expect(await otherToken.decimals()).to.be.bignumber.equal('18'); + }); + + it('has underlying', async function () { + expect(await this.token.underlying()).to.be.bignumber.equal(this.underlying.address); + }); + + describe('deposit', function () { + it('valid', async function () { + await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); + const { tx } = await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }); + await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { + from: initialHolder, + to: this.token.address, + value: initialSupply, + }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: ZERO_ADDRESS, + to: initialHolder, + value: initialSupply, + }); + }); + + it('missing approval', async function () { + await expectRevert( + this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }), + 'ERC20: insufficient allowance', + ); + }); + + it('missing balance', async function () { + await this.underlying.approve(this.token.address, MAX_UINT256, { from: initialHolder }); + await expectRevert( + this.token.depositFor(initialHolder, MAX_UINT256, { from: initialHolder }), + 'ERC20: transfer amount exceeds balance', + ); + }); + + it('to other account', async function () { + await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); + const { tx } = await this.token.depositFor(anotherAccount, initialSupply, { from: initialHolder }); + await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { + from: initialHolder, + to: this.token.address, + value: initialSupply, + }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: ZERO_ADDRESS, + to: anotherAccount, + value: initialSupply, + }); + }); + }); + + describe('withdraw', function () { + beforeEach(async function () { + await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); + await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }); + }); + + it('missing balance', async function () { + await expectRevert( + this.token.withdrawTo(initialHolder, MAX_UINT256, { from: initialHolder }), + 'ERC20: burn amount exceeds balance', + ); + }); + + it('valid', async function () { + const value = new BN(42); + + const { tx } = await this.token.withdrawTo(initialHolder, value, { from: initialHolder }); + await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { + from: this.token.address, + to: initialHolder, + value: value, + }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: initialHolder, + to: ZERO_ADDRESS, + value: value, + }); + }); + + it('entire balance', async function () { + const { tx } = await this.token.withdrawTo(initialHolder, initialSupply, { from: initialHolder }); + await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { + from: this.token.address, + to: initialHolder, + value: initialSupply, + }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: initialHolder, + to: ZERO_ADDRESS, + value: initialSupply, + }); + }); + + it('to other account', async function () { + const { tx } = await this.token.withdrawTo(anotherAccount, initialSupply, { from: initialHolder }); + await expectEvent.inTransaction(tx, this.underlying, 'Transfer', { + from: this.token.address, + to: anotherAccount, + value: initialSupply, + }); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: initialHolder, + to: ZERO_ADDRESS, + value: initialSupply, + }); + }); + }); + + describe('recover', function () { + it('nothing to recover', async function () { + await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); + await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }); + + const { tx } = await this.token.recover(anotherAccount); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: ZERO_ADDRESS, + to: anotherAccount, + value: '0', + }); + }); + + it('something to recover', async function () { + await this.underlying.transfer(this.token.address, initialSupply, { from: initialHolder }); + + const { tx } = await this.token.recover(anotherAccount); + await expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: ZERO_ADDRESS, + to: anotherAccount, + value: initialSupply, + }); + }); + }); + + describe('erc20 behaviour', function () { + beforeEach(async function () { + await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder }); + await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }); + }); + + shouldBehaveLikeERC20('ERC20', initialSupply, initialHolder, recipient, anotherAccount); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.t.sol b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.t.sol new file mode 100644 index 0000000..0aac0d4 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "erc4626-tests/ERC4626.test.sol"; + +import {SafeCast} from "../../../../contracts/utils/math/SafeCast.sol"; +import {ERC20Mock} from "../../../../contracts/mocks/ERC20Mock.sol"; +import {ERC4626Mock, IERC20Metadata} from "../../../../contracts/mocks/ERC4626Mock.sol"; + +contract ERC4626StdTest is ERC4626Test { + function setUp() public override { + _underlying_ = address(new ERC20Mock("MockERC20", "MockERC20", address(this), 0)); + _vault_ = address(new ERC4626Mock(IERC20Metadata(_underlying_), "MockERC4626", "MockERC4626")); + _delta_ = 0; + _vaultMayBeEmpty = false; + _unlimitedAmount = true; + } + + // solhint-disable-next-line func-name-mixedcase + function test_RT_mint_withdraw(ERC4626Test.Init memory init, uint256 shares) public override { + // There is an edge case where we currently behave different than the property tests, + // when all assets are lost to negative yield. + + // Sum all initially deposited assets. + int256 initAssets = 0; + for (uint256 i = 0; i < init.share.length; i++) { + vm.assume(init.share[i] <= uint256(type(int256).max - initAssets)); + initAssets += SafeCast.toInt256(init.share[i]); + } + + // Reject tests where the yield loses all assets from the vault. + vm.assume(init.yield > -initAssets); + + super.test_RT_mint_withdraw(init, shares); + } +} diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.test.js new file mode 100644 index 0000000..4e6986c --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/ERC4626.test.js @@ -0,0 +1,622 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const ERC20DecimalsMock = artifacts.require('ERC20DecimalsMock'); +const ERC4626Mock = artifacts.require('ERC4626Mock'); +const ERC4626DecimalMock = artifacts.require('ERC4626DecimalMock'); + +const parseToken = (token) => (new BN(token)).mul(new BN('1000000000000')); +const parseShare = (share) => (new BN(share)).mul(new BN('1000000000000000000')); + +contract('ERC4626', function (accounts) { + const [ holder, recipient, spender, other, user1, user2 ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + + beforeEach(async function () { + this.token = await ERC20DecimalsMock.new(name, symbol, 12); + this.vault = await ERC4626DecimalMock.new(this.token.address, name + ' Vault', symbol + 'V', 18); + + await this.token.mint(holder, web3.utils.toWei('100')); + await this.token.approve(this.vault.address, constants.MAX_UINT256, { from: holder }); + await this.vault.approve(spender, constants.MAX_UINT256, { from: holder }); + }); + + it('metadata', async function () { + expect(await this.vault.name()).to.be.equal(name + ' Vault'); + expect(await this.vault.symbol()).to.be.equal(symbol + 'V'); + expect(await this.vault.decimals()).to.be.bignumber.equal('18'); + expect(await this.vault.asset()).to.be.equal(this.token.address); + }); + + it('inherit decimals if from asset', async function () { + for (const decimals of [ 0, 9, 12, 18, 36 ].map(web3.utils.toBN)) { + const token = await ERC20DecimalsMock.new('', '', decimals); + const vault = await ERC4626Mock.new(token.address, '', ''); + expect(await vault.decimals()).to.be.bignumber.equal(decimals); + } + }); + + describe('empty vault: no assets & no shares', function () { + it('status', async function () { + expect(await this.vault.totalAssets()).to.be.bignumber.equal('0'); + }); + + it('deposit', async function () { + expect(await this.vault.maxDeposit(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewDeposit(parseToken(1))).to.be.bignumber.equal(parseShare(1)); + + const { tx } = await this.vault.deposit(parseToken(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(1), + }); + }); + + it('mint', async function () { + expect(await this.vault.maxMint(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewMint(parseShare(1))).to.be.bignumber.equal(parseToken(1)); + + const { tx } = await this.vault.mint(parseShare(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(1), + }); + }); + + it('withdraw', async function () { + expect(await this.vault.maxWithdraw(holder)).to.be.bignumber.equal('0'); + expect(await this.vault.previewWithdraw('0')).to.be.bignumber.equal('0'); + + const { tx } = await this.vault.withdraw('0', recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: '0', + }); + }); + + it('redeem', async function () { + expect(await this.vault.maxRedeem(holder)).to.be.bignumber.equal('0'); + expect(await this.vault.previewRedeem('0')).to.be.bignumber.equal('0'); + + const { tx } = await this.vault.redeem('0', recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: '0', + }); + }); + }); + + describe('partially empty vault: assets & no shares', function () { + beforeEach(async function () { + await this.token.mint(this.vault.address, parseToken(1)); // 1 token + }); + + it('status', async function () { + expect(await this.vault.totalAssets()).to.be.bignumber.equal(parseToken(1)); + }); + + it('deposit', async function () { + expect(await this.vault.maxDeposit(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewDeposit(parseToken(1))).to.be.bignumber.equal(parseShare(1)); + + const { tx } = await this.vault.deposit(parseToken(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(1), + }); + }); + + it('mint', async function () { + expect(await this.vault.maxMint(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewMint(parseShare(1))).to.be.bignumber.equal(parseToken(1)); + + const { tx } = await this.vault.mint(parseShare(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(1), + }); + }); + + it('withdraw', async function () { + expect(await this.vault.maxWithdraw(holder)).to.be.bignumber.equal('0'); + expect(await this.vault.previewWithdraw('0')).to.be.bignumber.equal('0'); + + const { tx } = await this.vault.withdraw('0', recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: '0', + }); + }); + + it('redeem', async function () { + expect(await this.vault.maxRedeem(holder)).to.be.bignumber.equal('0'); + expect(await this.vault.previewRedeem('0')).to.be.bignumber.equal('0'); + + const { tx } = await this.vault.redeem('0', recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: '0', + }); + }); + }); + + describe('partially empty vault: shares & no assets', function () { + beforeEach(async function () { + await this.vault.mockMint(holder, parseShare(1)); // 1 share + }); + + it('status', async function () { + expect(await this.vault.totalAssets()).to.be.bignumber.equal('0'); + }); + + it('deposit', async function () { + expect(await this.vault.maxDeposit(holder)).to.be.bignumber.equal('0'); + + // Can deposit 0 (max deposit) + const { tx } = await this.vault.deposit(0, recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: 0, + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: 0, + }); + + // Cannot deposit more than 0 + await expectRevert.unspecified(this.vault.previewDeposit(parseToken(1))); + await expectRevert( + this.vault.deposit(parseToken(1), recipient, { from: holder }), + 'ERC4626: deposit more than max', + ); + }); + + it('mint', async function () { + expect(await this.vault.maxMint(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewMint(parseShare(1))).to.be.bignumber.equal('0'); + + const { tx } = await this.vault.mint(parseShare(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(1), + }); + }); + + it('withdraw', async function () { + expect(await this.vault.maxWithdraw(holder)).to.be.bignumber.equal('0'); + expect(await this.vault.previewWithdraw('0')).to.be.bignumber.equal('0'); + await expectRevert.unspecified(this.vault.previewWithdraw('1')); + + const { tx } = await this.vault.withdraw('0', recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: '0', + }); + }); + + it('redeem', async function () { + expect(await this.vault.maxRedeem(holder)).to.be.bignumber.equal(parseShare(1)); + expect(await this.vault.previewRedeem(parseShare(1))).to.be.bignumber.equal('0'); + + const { tx } = await this.vault.redeem(parseShare(1), recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: '0', + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: parseShare(1), + }); + }); + }); + + describe('full vault: assets & shares', function () { + beforeEach(async function () { + await this.token.mint(this.vault.address, parseToken(1)); // 1 tokens + await this.vault.mockMint(holder, parseShare(100)); // 100 share + }); + + it('status', async function () { + expect(await this.vault.totalAssets()).to.be.bignumber.equal(parseToken(1)); + }); + + it('deposit', async function () { + expect(await this.vault.maxDeposit(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewDeposit(parseToken(1))).to.be.bignumber.equal(parseShare(100)); + + const { tx } = await this.vault.deposit(parseToken(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(100), + }); + }); + + it('mint', async function () { + expect(await this.vault.maxMint(holder)).to.be.bignumber.equal(constants.MAX_UINT256); + expect(await this.vault.previewMint(parseShare(1))).to.be.bignumber.equal(parseToken(1).divn(100)); + + const { tx } = await this.vault.mint(parseShare(1), recipient, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: holder, + to: this.vault.address, + value: parseToken(1).divn(100), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: recipient, + value: parseShare(1), + }); + }); + + it('withdraw', async function () { + expect(await this.vault.maxWithdraw(holder)).to.be.bignumber.equal(parseToken(1)); + expect(await this.vault.previewWithdraw(parseToken(1))).to.be.bignumber.equal(parseShare(100)); + + const { tx } = await this.vault.withdraw(parseToken(1), recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: parseShare(100), + }); + }); + + it('withdraw with approval', async function () { + await expectRevert( + this.vault.withdraw(parseToken(1), recipient, holder, { from: other }), + 'ERC20: insufficient allowance', + ); + + await this.vault.withdraw(parseToken(1), recipient, holder, { from: spender }); + }); + + it('redeem', async function () { + expect(await this.vault.maxRedeem(holder)).to.be.bignumber.equal(parseShare(100)); + expect(await this.vault.previewRedeem(parseShare(100))).to.be.bignumber.equal(parseToken(1)); + + const { tx } = await this.vault.redeem(parseShare(100), recipient, holder, { from: holder }); + + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: recipient, + value: parseToken(1), + }); + + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: holder, + to: constants.ZERO_ADDRESS, + value: parseShare(100), + }); + }); + + it('redeem with approval', async function () { + await expectRevert( + this.vault.redeem(parseShare(100), recipient, holder, { from: other }), + 'ERC20: insufficient allowance', + ); + + await this.vault.redeem(parseShare(100), recipient, holder, { from: spender }); + }); + }); + + /// Scenario inspired by solmate ERC4626 tests: + /// https://github.com/transmissions11/solmate/blob/main/src/test/ERC4626.t.sol + it('multiple mint, deposit, redeem & withdrawal', async function () { + // test designed with both asset using similar decimals + this.token = await ERC20DecimalsMock.new(name, symbol, 18); + this.vault = await ERC4626Mock.new(this.token.address, name + ' Vault', symbol + 'V'); + + await this.token.mint(user1, 4000); + await this.token.mint(user2, 7001); + await this.token.approve(this.vault.address, 4000, { from: user1 }); + await this.token.approve(this.vault.address, 7001, { from: user2 }); + + // 1. Alice mints 2000 shares (costs 2000 tokens) + { + const { tx } = await this.vault.mint(2000, user1, { from: user1 }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: user1, + to: this.vault.address, + value: '2000', + }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: user1, + value: '2000', + }); + + expect(await this.vault.previewDeposit(2000)).to.be.bignumber.equal('2000'); + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('2000'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('0'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('2000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('0'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('2000'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('2000'); + } + + // 2. Bob deposits 4000 tokens (mints 4000 shares) + { + const { tx } = await this.vault.mint(4000, user2, { from: user2 }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: user2, + to: this.vault.address, + value: '4000', + }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: user2, + value: '4000', + }); + + expect(await this.vault.previewDeposit(4000)).to.be.bignumber.equal('4000'); + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('2000'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('4000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('2000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('4000'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('6000'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('6000'); + } + + // 3. Vault mutates by +3000 tokens (simulated yield returned from strategy) + await this.token.mint(this.vault.address, 3000); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('2000'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('4000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('3000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('6000'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('6000'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('9000'); + + // 4. Alice deposits 2000 tokens (mints 1333 shares) + { + const { tx } = await this.vault.deposit(2000, user1, { from: user1 }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: user1, + to: this.vault.address, + value: '2000', + }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: user1, + value: '1333', + }); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('3333'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('4000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('4999'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('6000'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('7333'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('11000'); + } + + // 5. Bob mints 2000 shares (costs 3001 assets) + // NOTE: Bob's assets spent got rounded up + // NOTE: Alices's vault assets got rounded up + { + const { tx } = await this.vault.mint(2000, user2, { from: user2 }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: user2, + to: this.vault.address, + value: '3001', + }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: constants.ZERO_ADDRESS, + to: user2, + value: '2000', + }); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('3333'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('6000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('5000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('9000'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('9333'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('14001'); + } + + // 6. Vault mutates by +3000 tokens + // NOTE: Vault holds 17001 tokens, but sum of assetsOf() is 17000. + await this.token.mint(this.vault.address, 3000); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('3333'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('6000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('6071'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('10929'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('9333'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('17001'); + + // 7. Alice redeem 1333 shares (2428 assets) + { + const { tx } = await this.vault.redeem(1333, user1, user1, { from: user1 }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: user1, + to: constants.ZERO_ADDRESS, + value: '1333', + }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: user1, + value: '2428', + }); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('2000'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('6000'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('3643'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('10929'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('8000'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('14573'); + } + + // 8. Bob withdraws 2929 assets (1608 shares) + { + const { tx } = await this.vault.withdraw(2929, user2, user2, { from: user2 }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: user2, + to: constants.ZERO_ADDRESS, + value: '1608', + }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: user2, + value: '2929', + }); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('2000'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('4392'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('3643'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('8000'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('6392'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('11644'); + } + + // 9. Alice withdraws 3643 assets (2000 shares) + // NOTE: Bob's assets have been rounded back up + { + const { tx } = await this.vault.withdraw(3643, user1, user1, { from: user1 }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: user1, + to: constants.ZERO_ADDRESS, + value: '2000', + }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: user1, + value: '3643', + }); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('0'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('4392'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('0'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('8001'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('4392'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('8001'); + } + + // 10. Bob redeem 4392 shares (8001 tokens) + { + const { tx } = await this.vault.redeem(4392, user2, user2, { from: user2 }); + expectEvent.inTransaction(tx, this.vault, 'Transfer', { + from: user2, + to: constants.ZERO_ADDRESS, + value: '4392', + }); + expectEvent.inTransaction(tx, this.token, 'Transfer', { + from: this.vault.address, + to: user2, + value: '8001', + }); + + expect(await this.vault.balanceOf(user1)).to.be.bignumber.equal('0'); + expect(await this.vault.balanceOf(user2)).to.be.bignumber.equal('0'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user1))).to.be.bignumber.equal('0'); + expect(await this.vault.convertToAssets(await this.vault.balanceOf(user2))).to.be.bignumber.equal('0'); + expect(await this.vault.totalSupply()).to.be.bignumber.equal('0'); + expect(await this.vault.totalAssets()).to.be.bignumber.equal('0'); + } + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/extensions/draft-ERC20Permit.test.js b/lib/openzeppelin-contracts/test/token/ERC20/extensions/draft-ERC20Permit.test.js new file mode 100644 index 0000000..26d0c84 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/extensions/draft-ERC20Permit.test.js @@ -0,0 +1,109 @@ +/* eslint-disable */ + +const { BN, constants, expectRevert, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { MAX_UINT256 } = constants; + +const { fromRpcSig } = require('ethereumjs-util'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; + +const ERC20PermitMock = artifacts.require('ERC20PermitMock'); + +const { EIP712Domain, Permit, domainSeparator } = require('../../../helpers/eip712'); + +contract('ERC20Permit', function (accounts) { + const [ initialHolder, spender ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + const version = '1'; + + const initialSupply = new BN(100); + + beforeEach(async function () { + this.token = await ERC20PermitMock.new(name, symbol, initialHolder, initialSupply); + + // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id + // from within the EVM as from the JSON RPC interface. + // See https://github.com/trufflesuite/ganache-core/issues/515 + this.chainId = await this.token.getChainId(); + }); + + it('initial nonce is 0', async function () { + expect(await this.token.nonces(initialHolder)).to.be.bignumber.equal('0'); + }); + + it('domain separator', async function () { + expect( + await this.token.DOMAIN_SEPARATOR(), + ).to.equal( + await domainSeparator(name, version, this.chainId, this.token.address), + ); + }); + + describe('permit', function () { + const wallet = Wallet.generate(); + + const owner = wallet.getAddressString(); + const value = new BN(42); + const nonce = 0; + const maxDeadline = MAX_UINT256; + + const buildData = (chainId, verifyingContract, deadline = maxDeadline) => ({ + primaryType: 'Permit', + types: { EIP712Domain, Permit }, + domain: { name, version, chainId, verifyingContract }, + message: { owner, spender, value, nonce, deadline }, + }); + + it('accepts owner signature', async function () { + const data = buildData(this.chainId, this.token.address); + const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }); + const { v, r, s } = fromRpcSig(signature); + + await this.token.permit(owner, spender, value, maxDeadline, v, r, s); + + expect(await this.token.nonces(owner)).to.be.bignumber.equal('1'); + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(value); + }); + + it('rejects reused signature', async function () { + const data = buildData(this.chainId, this.token.address); + const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }); + const { v, r, s } = fromRpcSig(signature); + + await this.token.permit(owner, spender, value, maxDeadline, v, r, s); + + await expectRevert( + this.token.permit(owner, spender, value, maxDeadline, v, r, s), + 'ERC20Permit: invalid signature', + ); + }); + + it('rejects other signature', async function () { + const otherWallet = Wallet.generate(); + const data = buildData(this.chainId, this.token.address); + const signature = ethSigUtil.signTypedMessage(otherWallet.getPrivateKey(), { data }); + const { v, r, s } = fromRpcSig(signature); + + await expectRevert( + this.token.permit(owner, spender, value, maxDeadline, v, r, s), + 'ERC20Permit: invalid signature', + ); + }); + + it('rejects expired permit', async function () { + const deadline = (await time.latest()) - time.duration.weeks(1); + + const data = buildData(this.chainId, this.token.address, deadline); + const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }); + const { v, r, s } = fromRpcSig(signature); + + await expectRevert( + this.token.permit(owner, spender, value, deadline, v, r, s), + 'ERC20Permit: expired deadline', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/presets/ERC20PresetFixedSupply.test.js b/lib/openzeppelin-contracts/test/token/ERC20/presets/ERC20PresetFixedSupply.test.js new file mode 100644 index 0000000..f1d0b53 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/presets/ERC20PresetFixedSupply.test.js @@ -0,0 +1,42 @@ +const { BN, constants, expectEvent } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const ERC20PresetFixedSupply = artifacts.require('ERC20PresetFixedSupply'); + +contract('ERC20PresetFixedSupply', function (accounts) { + const [deployer, owner] = accounts; + + const name = 'PresetFixedSupply'; + const symbol = 'PFS'; + + const initialSupply = new BN('50000'); + const amount = new BN('10000'); + + before(async function () { + this.token = await ERC20PresetFixedSupply.new(name, symbol, initialSupply, owner, { from: deployer }); + }); + + it('deployer has the balance equal to initial supply', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal(initialSupply); + }); + + it('total supply is equal to initial supply', async function () { + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); + }); + + describe('burning', function () { + it('holders can burn their tokens', async function () { + const remainingBalance = initialSupply.sub(amount); + const receipt = await this.token.burn(amount, { from: owner }); + expectEvent(receipt, 'Transfer', { from: owner, to: ZERO_ADDRESS, value: amount }); + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal(remainingBalance); + }); + + it('decrements totalSupply', async function () { + const expectedSupply = initialSupply.sub(amount); + expect(await this.token.totalSupply()).to.be.bignumber.equal(expectedSupply); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/presets/ERC20PresetMinterPauser.test.js b/lib/openzeppelin-contracts/test/token/ERC20/presets/ERC20PresetMinterPauser.test.js new file mode 100644 index 0000000..c143790 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/presets/ERC20PresetMinterPauser.test.js @@ -0,0 +1,113 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const ERC20PresetMinterPauser = artifacts.require('ERC20PresetMinterPauser'); + +contract('ERC20PresetMinterPauser', function (accounts) { + const [ deployer, other ] = accounts; + + const name = 'MinterPauserToken'; + const symbol = 'DRT'; + + const amount = new BN('5000'); + + const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const MINTER_ROLE = web3.utils.soliditySha3('MINTER_ROLE'); + const PAUSER_ROLE = web3.utils.soliditySha3('PAUSER_ROLE'); + + beforeEach(async function () { + this.token = await ERC20PresetMinterPauser.new(name, symbol, { from: deployer }); + }); + + it('deployer has the default admin role', async function () { + expect(await this.token.getRoleMemberCount(DEFAULT_ADMIN_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(DEFAULT_ADMIN_ROLE, 0)).to.equal(deployer); + }); + + it('deployer has the minter role', async function () { + expect(await this.token.getRoleMemberCount(MINTER_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(MINTER_ROLE, 0)).to.equal(deployer); + }); + + it('deployer has the pauser role', async function () { + expect(await this.token.getRoleMemberCount(PAUSER_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(PAUSER_ROLE, 0)).to.equal(deployer); + }); + + it('minter and pauser role admin is the default admin', async function () { + expect(await this.token.getRoleAdmin(MINTER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + expect(await this.token.getRoleAdmin(PAUSER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + }); + + describe('minting', function () { + it('deployer can mint tokens', async function () { + const receipt = await this.token.mint(other, amount, { from: deployer }); + expectEvent(receipt, 'Transfer', { from: ZERO_ADDRESS, to: other, value: amount }); + + expect(await this.token.balanceOf(other)).to.be.bignumber.equal(amount); + }); + + it('other accounts cannot mint tokens', async function () { + await expectRevert( + this.token.mint(other, amount, { from: other }), + 'ERC20PresetMinterPauser: must have minter role to mint', + ); + }); + }); + + describe('pausing', function () { + it('deployer can pause', async function () { + const receipt = await this.token.pause({ from: deployer }); + expectEvent(receipt, 'Paused', { account: deployer }); + + expect(await this.token.paused()).to.equal(true); + }); + + it('deployer can unpause', async function () { + await this.token.pause({ from: deployer }); + + const receipt = await this.token.unpause({ from: deployer }); + expectEvent(receipt, 'Unpaused', { account: deployer }); + + expect(await this.token.paused()).to.equal(false); + }); + + it('cannot mint while paused', async function () { + await this.token.pause({ from: deployer }); + + await expectRevert( + this.token.mint(other, amount, { from: deployer }), + 'ERC20Pausable: token transfer while paused', + ); + }); + + it('other accounts cannot pause', async function () { + await expectRevert( + this.token.pause({ from: other }), + 'ERC20PresetMinterPauser: must have pauser role to pause', + ); + }); + + it('other accounts cannot unpause', async function () { + await this.token.pause({ from: deployer }); + + await expectRevert( + this.token.unpause({ from: other }), + 'ERC20PresetMinterPauser: must have pauser role to unpause', + ); + }); + }); + + describe('burning', function () { + it('holders can burn their tokens', async function () { + await this.token.mint(other, amount, { from: deployer }); + + const receipt = await this.token.burn(amount.subn(1), { from: other }); + expectEvent(receipt, 'Transfer', { from: other, to: ZERO_ADDRESS, value: amount.subn(1) }); + + expect(await this.token.balanceOf(other)).to.be.bignumber.equal('1'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC20/utils/SafeERC20.test.js b/lib/openzeppelin-contracts/test/token/ERC20/utils/SafeERC20.test.js new file mode 100644 index 0000000..4c54c58 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/utils/SafeERC20.test.js @@ -0,0 +1,255 @@ +const { constants, expectRevert } = require('@openzeppelin/test-helpers'); + +const ERC20ReturnFalseMock = artifacts.require('ERC20ReturnFalseMock'); +const ERC20ReturnTrueMock = artifacts.require('ERC20ReturnTrueMock'); +const ERC20NoReturnMock = artifacts.require('ERC20NoReturnMock'); +const ERC20PermitNoRevertMock = artifacts.require('ERC20PermitNoRevertMock'); +const SafeERC20Wrapper = artifacts.require('SafeERC20Wrapper'); + +const { EIP712Domain, Permit } = require('../../../helpers/eip712'); + +const { fromRpcSig } = require('ethereumjs-util'); +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; + +contract('SafeERC20', function (accounts) { + const [ hasNoCode ] = accounts; + + describe('with address that has no contract code', function () { + beforeEach(async function () { + this.wrapper = await SafeERC20Wrapper.new(hasNoCode); + }); + + shouldRevertOnAllCalls('Address: call to non-contract'); + }); + + describe('with token that returns false on all calls', function () { + beforeEach(async function () { + this.wrapper = await SafeERC20Wrapper.new((await ERC20ReturnFalseMock.new()).address); + }); + + shouldRevertOnAllCalls('SafeERC20: ERC20 operation did not succeed'); + }); + + describe('with token that returns true on all calls', function () { + beforeEach(async function () { + this.wrapper = await SafeERC20Wrapper.new((await ERC20ReturnTrueMock.new()).address); + }); + + shouldOnlyRevertOnErrors(); + }); + + describe('with token that returns no boolean values', function () { + beforeEach(async function () { + this.wrapper = await SafeERC20Wrapper.new((await ERC20NoReturnMock.new()).address); + }); + + shouldOnlyRevertOnErrors(); + }); + + describe('with token that doesn\'t revert on invalid permit', function () { + const wallet = Wallet.generate(); + const owner = wallet.getAddressString(); + const spender = hasNoCode; + + beforeEach(async function () { + this.token = await ERC20PermitNoRevertMock.new(); + this.wrapper = await SafeERC20Wrapper.new(this.token.address); + + const chainId = await this.token.getChainId(); + + this.data = { + primaryType: 'Permit', + types: { EIP712Domain, Permit }, + domain: { name: 'ERC20PermitNoRevertMock', version: '1', chainId, verifyingContract: this.token.address }, + message: { owner, spender, value: '42', nonce: '0', deadline: constants.MAX_UINT256 }, + }; + this.signature = fromRpcSig(ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data: this.data })); + }); + + it('accepts owner signature', async function () { + expect(await this.token.nonces(owner)).to.be.bignumber.equal('0'); + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal('0'); + + await this.wrapper.permit( + this.data.message.owner, + this.data.message.spender, + this.data.message.value, + this.data.message.deadline, + this.signature.v, + this.signature.r, + this.signature.s, + ); + + expect(await this.token.nonces(owner)).to.be.bignumber.equal('1'); + expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(this.data.message.value); + }); + + it('revert on reused signature', async function () { + expect(await this.token.nonces(owner)).to.be.bignumber.equal('0'); + // use valid signature and consume nounce + await this.wrapper.permit( + this.data.message.owner, + this.data.message.spender, + this.data.message.value, + this.data.message.deadline, + this.signature.v, + this.signature.r, + this.signature.s, + ); + expect(await this.token.nonces(owner)).to.be.bignumber.equal('1'); + // invalid call does not revert for this token implementation + await this.token.permit( + this.data.message.owner, + this.data.message.spender, + this.data.message.value, + this.data.message.deadline, + this.signature.v, + this.signature.r, + this.signature.s, + ); + expect(await this.token.nonces(owner)).to.be.bignumber.equal('1'); + // invalid call revert when called through the SafeERC20 library + await expectRevert( + this.wrapper.permit( + this.data.message.owner, + this.data.message.spender, + this.data.message.value, + this.data.message.deadline, + this.signature.v, + this.signature.r, + this.signature.s, + ), + 'SafeERC20: permit did not succeed', + ); + expect(await this.token.nonces(owner)).to.be.bignumber.equal('1'); + }); + + it('revert on invalid signature', async function () { + // signature that is not valid for owner + const invalidSignature = { + v: 27, + r: '0x71753dc5ecb5b4bfc0e3bc530d79ce5988760ed3f3a234c86a5546491f540775', + s: '0x0049cedee5aed990aabed5ad6a9f6e3c565b63379894b5fa8b512eb2b79e485d', + }; + + // invalid call does not revert for this token implementation + await this.token.permit( + this.data.message.owner, + this.data.message.spender, + this.data.message.value, + this.data.message.deadline, + invalidSignature.v, + invalidSignature.r, + invalidSignature.s, + ); + + // invalid call revert when called through the SafeERC20 library + await expectRevert( + this.wrapper.permit( + this.data.message.owner, + this.data.message.spender, + this.data.message.value, + this.data.message.deadline, + invalidSignature.v, + invalidSignature.r, + invalidSignature.s, + ), + 'SafeERC20: permit did not succeed', + ); + }); + }); +}); + +function shouldRevertOnAllCalls (reason) { + it('reverts on transfer', async function () { + await expectRevert(this.wrapper.transfer(), reason); + }); + + it('reverts on transferFrom', async function () { + await expectRevert(this.wrapper.transferFrom(), reason); + }); + + it('reverts on approve', async function () { + await expectRevert(this.wrapper.approve(0), reason); + }); + + it('reverts on increaseAllowance', async function () { + // [TODO] make sure it's reverting for the right reason + await expectRevert.unspecified(this.wrapper.increaseAllowance(0)); + }); + + it('reverts on decreaseAllowance', async function () { + // [TODO] make sure it's reverting for the right reason + await expectRevert.unspecified(this.wrapper.decreaseAllowance(0)); + }); +} + +function shouldOnlyRevertOnErrors () { + it('doesn\'t revert on transfer', async function () { + await this.wrapper.transfer(); + }); + + it('doesn\'t revert on transferFrom', async function () { + await this.wrapper.transferFrom(); + }); + + describe('approvals', function () { + context('with zero allowance', function () { + beforeEach(async function () { + await this.wrapper.setAllowance(0); + }); + + it('doesn\'t revert when approving a non-zero allowance', async function () { + await this.wrapper.approve(100); + }); + + it('doesn\'t revert when approving a zero allowance', async function () { + await this.wrapper.approve(0); + }); + + it('doesn\'t revert when increasing the allowance', async function () { + await this.wrapper.increaseAllowance(10); + }); + + it('reverts when decreasing the allowance', async function () { + await expectRevert( + this.wrapper.decreaseAllowance(10), + 'SafeERC20: decreased allowance below zero', + ); + }); + }); + + context('with non-zero allowance', function () { + beforeEach(async function () { + await this.wrapper.setAllowance(100); + }); + + it('reverts when approving a non-zero allowance', async function () { + await expectRevert( + this.wrapper.approve(20), + 'SafeERC20: approve from non-zero to non-zero allowance', + ); + }); + + it('doesn\'t revert when approving a zero allowance', async function () { + await this.wrapper.approve(0); + }); + + it('doesn\'t revert when increasing the allowance', async function () { + await this.wrapper.increaseAllowance(10); + }); + + it('doesn\'t revert when decreasing the allowance to a positive value', async function () { + await this.wrapper.decreaseAllowance(50); + }); + + it('reverts when decreasing the allowance to a negative value', async function () { + await expectRevert( + this.wrapper.decreaseAllowance(200), + 'SafeERC20: decreased allowance below zero', + ); + }); + }); + }); +} diff --git a/lib/openzeppelin-contracts/test/token/ERC20/utils/TokenTimelock.test.js b/lib/openzeppelin-contracts/test/token/ERC20/utils/TokenTimelock.test.js new file mode 100644 index 0000000..e546b34 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC20/utils/TokenTimelock.test.js @@ -0,0 +1,71 @@ +const { BN, expectRevert, time } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC20Mock = artifacts.require('ERC20Mock'); +const TokenTimelock = artifacts.require('TokenTimelock'); + +contract('TokenTimelock', function (accounts) { + const [ beneficiary ] = accounts; + + const name = 'My Token'; + const symbol = 'MTKN'; + + const amount = new BN(100); + + context('with token', function () { + beforeEach(async function () { + this.token = await ERC20Mock.new(name, symbol, beneficiary, 0); // We're not using the preminted tokens + }); + + it('rejects a release time in the past', async function () { + const pastReleaseTime = (await time.latest()).sub(time.duration.years(1)); + await expectRevert( + TokenTimelock.new(this.token.address, beneficiary, pastReleaseTime), + 'TokenTimelock: release time is before current time', + ); + }); + + context('once deployed', function () { + beforeEach(async function () { + this.releaseTime = (await time.latest()).add(time.duration.years(1)); + this.timelock = await TokenTimelock.new(this.token.address, beneficiary, this.releaseTime); + await this.token.mint(this.timelock.address, amount); + }); + + it('can get state', async function () { + expect(await this.timelock.token()).to.equal(this.token.address); + expect(await this.timelock.beneficiary()).to.equal(beneficiary); + expect(await this.timelock.releaseTime()).to.be.bignumber.equal(this.releaseTime); + }); + + it('cannot be released before time limit', async function () { + await expectRevert(this.timelock.release(), 'TokenTimelock: current time is before release time'); + }); + + it('cannot be released just before time limit', async function () { + await time.increaseTo(this.releaseTime.sub(time.duration.seconds(3))); + await expectRevert(this.timelock.release(), 'TokenTimelock: current time is before release time'); + }); + + it('can be released just after limit', async function () { + await time.increaseTo(this.releaseTime.add(time.duration.seconds(1))); + await this.timelock.release(); + expect(await this.token.balanceOf(beneficiary)).to.be.bignumber.equal(amount); + }); + + it('can be released after time limit', async function () { + await time.increaseTo(this.releaseTime.add(time.duration.years(1))); + await this.timelock.release(); + expect(await this.token.balanceOf(beneficiary)).to.be.bignumber.equal(amount); + }); + + it('cannot be released twice', async function () { + await time.increaseTo(this.releaseTime.add(time.duration.years(1))); + await this.timelock.release(); + await expectRevert(this.timelock.release(), 'TokenTimelock: no tokens to release'); + expect(await this.token.balanceOf(beneficiary)).to.be.bignumber.equal(amount); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/ERC721.behavior.js b/lib/openzeppelin-contracts/test/token/ERC721/ERC721.behavior.js new file mode 100644 index 0000000..0213984 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/ERC721.behavior.js @@ -0,0 +1,937 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { ZERO_ADDRESS } = constants; + +const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); + +const ERC721ReceiverMock = artifacts.require('ERC721ReceiverMock'); + +const Error = [ 'None', 'RevertWithMessage', 'RevertWithoutMessage', 'Panic' ] + .reduce((acc, entry, idx) => Object.assign({ [entry]: idx }, acc), {}); + +const firstTokenId = new BN('5042'); +const secondTokenId = new BN('79217'); +const nonExistentTokenId = new BN('13'); +const fourthTokenId = new BN(4); +const baseURI = 'https://api.example.com/v1/'; + +const RECEIVER_MAGIC_VALUE = '0x150b7a02'; + +function shouldBehaveLikeERC721 (errorPrefix, owner, newOwner, approved, anotherApproved, operator, other) { + shouldSupportInterfaces([ + 'ERC165', + 'ERC721', + ]); + + context('with minted tokens', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + await this.token.mint(owner, secondTokenId); + this.toWhom = other; // default to other for toWhom in context-dependent tests + }); + + describe('balanceOf', function () { + context('when the given address owns some tokens', function () { + it('returns the amount of tokens owned by the given address', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('2'); + }); + }); + + context('when the given address does not own any tokens', function () { + it('returns 0', async function () { + expect(await this.token.balanceOf(other)).to.be.bignumber.equal('0'); + }); + }); + + context('when querying the zero address', function () { + it('throws', async function () { + await expectRevert( + this.token.balanceOf(ZERO_ADDRESS), 'ERC721: address zero is not a valid owner', + ); + }); + }); + }); + + describe('ownerOf', function () { + context('when the given token ID was tracked by this token', function () { + const tokenId = firstTokenId; + + it('returns the owner of the given token ID', async function () { + expect(await this.token.ownerOf(tokenId)).to.be.equal(owner); + }); + }); + + context('when the given token ID was not tracked by this token', function () { + const tokenId = nonExistentTokenId; + + it('reverts', async function () { + await expectRevert( + this.token.ownerOf(tokenId), 'ERC721: invalid token ID', + ); + }); + }); + }); + + describe('transfers', function () { + const tokenId = firstTokenId; + const data = '0x42'; + + let receipt = null; + + beforeEach(async function () { + await this.token.approve(approved, tokenId, { from: owner }); + await this.token.setApprovalForAll(operator, true, { from: owner }); + }); + + const transferWasSuccessful = function ({ owner, tokenId, approved }) { + it('transfers the ownership of the given token ID to the given address', async function () { + expect(await this.token.ownerOf(tokenId)).to.be.equal(this.toWhom); + }); + + it('emits a Transfer event', async function () { + expectEvent(receipt, 'Transfer', { from: owner, to: this.toWhom, tokenId: tokenId }); + }); + + it('clears the approval for the token ID', async function () { + expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS); + }); + + it('adjusts owners balances', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1'); + }); + + it('adjusts owners tokens by index', async function () { + if (!this.token.tokenOfOwnerByIndex) return; + + expect(await this.token.tokenOfOwnerByIndex(this.toWhom, 0)).to.be.bignumber.equal(tokenId); + + expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.not.equal(tokenId); + }); + }; + + const shouldTransferTokensByUsers = function (transferFunction) { + context('when called by the owner', function () { + beforeEach(async function () { + (receipt = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: owner })); + }); + transferWasSuccessful({ owner, tokenId, approved }); + }); + + context('when called by the approved individual', function () { + beforeEach(async function () { + (receipt = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: approved })); + }); + transferWasSuccessful({ owner, tokenId, approved }); + }); + + context('when called by the operator', function () { + beforeEach(async function () { + (receipt = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: operator })); + }); + transferWasSuccessful({ owner, tokenId, approved }); + }); + + context('when called by the owner without an approved user', function () { + beforeEach(async function () { + await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner }); + (receipt = await transferFunction.call(this, owner, this.toWhom, tokenId, { from: operator })); + }); + transferWasSuccessful({ owner, tokenId, approved: null }); + }); + + context('when sent to the owner', function () { + beforeEach(async function () { + (receipt = await transferFunction.call(this, owner, owner, tokenId, { from: owner })); + }); + + it('keeps ownership of the token', async function () { + expect(await this.token.ownerOf(tokenId)).to.be.equal(owner); + }); + + it('clears the approval for the token ID', async function () { + expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS); + }); + + it('emits only a transfer event', async function () { + expectEvent(receipt, 'Transfer', { + from: owner, + to: owner, + tokenId: tokenId, + }); + }); + + it('keeps the owner balance', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('2'); + }); + + it('keeps same tokens by index', async function () { + if (!this.token.tokenOfOwnerByIndex) return; + const tokensListed = await Promise.all( + [0, 1].map(i => this.token.tokenOfOwnerByIndex(owner, i)), + ); + expect(tokensListed.map(t => t.toNumber())).to.have.members( + [firstTokenId.toNumber(), secondTokenId.toNumber()], + ); + }); + }); + + context('when the address of the previous owner is incorrect', function () { + it('reverts', async function () { + await expectRevert( + transferFunction.call(this, other, other, tokenId, { from: owner }), + 'ERC721: transfer from incorrect owner', + ); + }); + }); + + context('when the sender is not authorized for the token id', function () { + it('reverts', async function () { + await expectRevert( + transferFunction.call(this, owner, other, tokenId, { from: other }), + 'ERC721: caller is not token owner or approved', + ); + }); + }); + + context('when the given token ID does not exist', function () { + it('reverts', async function () { + await expectRevert( + transferFunction.call(this, owner, other, nonExistentTokenId, { from: owner }), + 'ERC721: invalid token ID', + ); + }); + }); + + context('when the address to transfer the token to is the zero address', function () { + it('reverts', async function () { + await expectRevert( + transferFunction.call(this, owner, ZERO_ADDRESS, tokenId, { from: owner }), + 'ERC721: transfer to the zero address', + ); + }); + }); + }; + + describe('via transferFrom', function () { + shouldTransferTokensByUsers(function (from, to, tokenId, opts) { + return this.token.transferFrom(from, to, tokenId, opts); + }); + }); + + describe('via safeTransferFrom', function () { + const safeTransferFromWithData = function (from, to, tokenId, opts) { + return this.token.methods['safeTransferFrom(address,address,uint256,bytes)'](from, to, tokenId, data, opts); + }; + + const safeTransferFromWithoutData = function (from, to, tokenId, opts) { + return this.token.methods['safeTransferFrom(address,address,uint256)'](from, to, tokenId, opts); + }; + + const shouldTransferSafely = function (transferFun, data) { + describe('to a user account', function () { + shouldTransferTokensByUsers(transferFun); + }); + + describe('to a valid receiver contract', function () { + beforeEach(async function () { + this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None); + this.toWhom = this.receiver.address; + }); + + shouldTransferTokensByUsers(transferFun); + + it('calls onERC721Received', async function () { + const receipt = await transferFun.call(this, owner, this.receiver.address, tokenId, { from: owner }); + + await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', { + operator: owner, + from: owner, + tokenId: tokenId, + data: data, + }); + }); + + it('calls onERC721Received from approved', async function () { + const receipt = await transferFun.call(this, owner, this.receiver.address, tokenId, { from: approved }); + + await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', { + operator: approved, + from: owner, + tokenId: tokenId, + data: data, + }); + }); + + describe('with an invalid token id', function () { + it('reverts', async function () { + await expectRevert( + transferFun.call( + this, + owner, + this.receiver.address, + nonExistentTokenId, + { from: owner }, + ), + 'ERC721: invalid token ID', + ); + }); + }); + }); + }; + + describe('with data', function () { + shouldTransferSafely(safeTransferFromWithData, data); + }); + + describe('without data', function () { + shouldTransferSafely(safeTransferFromWithoutData, null); + }); + + describe('to a receiver contract returning unexpected value', function () { + it('reverts', async function () { + const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None); + await expectRevert( + this.token.safeTransferFrom(owner, invalidReceiver.address, tokenId, { from: owner }), + 'ERC721: transfer to non ERC721Receiver implementer', + ); + }); + }); + + describe('to a receiver contract that reverts with message', function () { + it('reverts', async function () { + const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage); + await expectRevert( + this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }), + 'ERC721ReceiverMock: reverting', + ); + }); + }); + + describe('to a receiver contract that reverts without message', function () { + it('reverts', async function () { + const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage); + await expectRevert( + this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }), + 'ERC721: transfer to non ERC721Receiver implementer', + ); + }); + }); + + describe('to a receiver contract that panics', function () { + it('reverts', async function () { + const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic); + await expectRevert.unspecified( + this.token.safeTransferFrom(owner, revertingReceiver.address, tokenId, { from: owner }), + ); + }); + }); + + describe('to a contract that does not implement the required function', function () { + it('reverts', async function () { + const nonReceiver = this.token; + await expectRevert( + this.token.safeTransferFrom(owner, nonReceiver.address, tokenId, { from: owner }), + 'ERC721: transfer to non ERC721Receiver implementer', + ); + }); + }); + }); + }); + + describe('safe mint', function () { + const tokenId = fourthTokenId; + const data = '0x42'; + + describe('via safeMint', function () { // regular minting is tested in ERC721Mintable.test.js and others + it('calls onERC721Received — with data', async function () { + this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None); + const receipt = await this.token.safeMint(this.receiver.address, tokenId, data); + + await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', { + from: ZERO_ADDRESS, + tokenId: tokenId, + data: data, + }); + }); + + it('calls onERC721Received — without data', async function () { + this.receiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.None); + const receipt = await this.token.safeMint(this.receiver.address, tokenId); + + await expectEvent.inTransaction(receipt.tx, ERC721ReceiverMock, 'Received', { + from: ZERO_ADDRESS, + tokenId: tokenId, + }); + }); + + context('to a receiver contract returning unexpected value', function () { + it('reverts', async function () { + const invalidReceiver = await ERC721ReceiverMock.new('0x42', Error.None); + await expectRevert( + this.token.safeMint(invalidReceiver.address, tokenId), + 'ERC721: transfer to non ERC721Receiver implementer', + ); + }); + }); + + context('to a receiver contract that reverts with message', function () { + it('reverts', async function () { + const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithMessage); + await expectRevert( + this.token.safeMint(revertingReceiver.address, tokenId), + 'ERC721ReceiverMock: reverting', + ); + }); + }); + + context('to a receiver contract that reverts without message', function () { + it('reverts', async function () { + const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.RevertWithoutMessage); + await expectRevert( + this.token.safeMint(revertingReceiver.address, tokenId), + 'ERC721: transfer to non ERC721Receiver implementer', + ); + }); + }); + + context('to a receiver contract that panics', function () { + it('reverts', async function () { + const revertingReceiver = await ERC721ReceiverMock.new(RECEIVER_MAGIC_VALUE, Error.Panic); + await expectRevert.unspecified( + this.token.safeMint(revertingReceiver.address, tokenId), + ); + }); + }); + + context('to a contract that does not implement the required function', function () { + it('reverts', async function () { + const nonReceiver = this.token; + await expectRevert( + this.token.safeMint(nonReceiver.address, tokenId), + 'ERC721: transfer to non ERC721Receiver implementer', + ); + }); + }); + }); + }); + + describe('approve', function () { + const tokenId = firstTokenId; + + let receipt = null; + + const itClearsApproval = function () { + it('clears approval for the token', async function () { + expect(await this.token.getApproved(tokenId)).to.be.equal(ZERO_ADDRESS); + }); + }; + + const itApproves = function (address) { + it('sets the approval for the target address', async function () { + expect(await this.token.getApproved(tokenId)).to.be.equal(address); + }); + }; + + const itEmitsApprovalEvent = function (address) { + it('emits an approval event', async function () { + expectEvent(receipt, 'Approval', { + owner: owner, + approved: address, + tokenId: tokenId, + }); + }); + }; + + context('when clearing approval', function () { + context('when there was no prior approval', function () { + beforeEach(async function () { + (receipt = await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner })); + }); + + itClearsApproval(); + itEmitsApprovalEvent(ZERO_ADDRESS); + }); + + context('when there was a prior approval', function () { + beforeEach(async function () { + await this.token.approve(approved, tokenId, { from: owner }); + (receipt = await this.token.approve(ZERO_ADDRESS, tokenId, { from: owner })); + }); + + itClearsApproval(); + itEmitsApprovalEvent(ZERO_ADDRESS); + }); + }); + + context('when approving a non-zero address', function () { + context('when there was no prior approval', function () { + beforeEach(async function () { + (receipt = await this.token.approve(approved, tokenId, { from: owner })); + }); + + itApproves(approved); + itEmitsApprovalEvent(approved); + }); + + context('when there was a prior approval to the same address', function () { + beforeEach(async function () { + await this.token.approve(approved, tokenId, { from: owner }); + (receipt = await this.token.approve(approved, tokenId, { from: owner })); + }); + + itApproves(approved); + itEmitsApprovalEvent(approved); + }); + + context('when there was a prior approval to a different address', function () { + beforeEach(async function () { + await this.token.approve(anotherApproved, tokenId, { from: owner }); + (receipt = await this.token.approve(anotherApproved, tokenId, { from: owner })); + }); + + itApproves(anotherApproved); + itEmitsApprovalEvent(anotherApproved); + }); + }); + + context('when the address that receives the approval is the owner', function () { + it('reverts', async function () { + await expectRevert( + this.token.approve(owner, tokenId, { from: owner }), 'ERC721: approval to current owner', + ); + }); + }); + + context('when the sender does not own the given token ID', function () { + it('reverts', async function () { + await expectRevert(this.token.approve(approved, tokenId, { from: other }), + 'ERC721: approve caller is not token owner or approved'); + }); + }); + + context('when the sender is approved for the given token ID', function () { + it('reverts', async function () { + await this.token.approve(approved, tokenId, { from: owner }); + await expectRevert(this.token.approve(anotherApproved, tokenId, { from: approved }), + 'ERC721: approve caller is not token owner or approved for all'); + }); + }); + + context('when the sender is an operator', function () { + beforeEach(async function () { + await this.token.setApprovalForAll(operator, true, { from: owner }); + (receipt = await this.token.approve(approved, tokenId, { from: operator })); + }); + + itApproves(approved); + itEmitsApprovalEvent(approved); + }); + + context('when the given token ID does not exist', function () { + it('reverts', async function () { + await expectRevert(this.token.approve(approved, nonExistentTokenId, { from: operator }), + 'ERC721: invalid token ID'); + }); + }); + }); + + describe('setApprovalForAll', function () { + context('when the operator willing to approve is not the owner', function () { + context('when there is no operator approval set by the sender', function () { + it('approves the operator', async function () { + await this.token.setApprovalForAll(operator, true, { from: owner }); + + expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true); + }); + + it('emits an approval event', async function () { + const receipt = await this.token.setApprovalForAll(operator, true, { from: owner }); + + expectEvent(receipt, 'ApprovalForAll', { + owner: owner, + operator: operator, + approved: true, + }); + }); + }); + + context('when the operator was set as not approved', function () { + beforeEach(async function () { + await this.token.setApprovalForAll(operator, false, { from: owner }); + }); + + it('approves the operator', async function () { + await this.token.setApprovalForAll(operator, true, { from: owner }); + + expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true); + }); + + it('emits an approval event', async function () { + const receipt = await this.token.setApprovalForAll(operator, true, { from: owner }); + + expectEvent(receipt, 'ApprovalForAll', { + owner: owner, + operator: operator, + approved: true, + }); + }); + + it('can unset the operator approval', async function () { + await this.token.setApprovalForAll(operator, false, { from: owner }); + + expect(await this.token.isApprovedForAll(owner, operator)).to.equal(false); + }); + }); + + context('when the operator was already approved', function () { + beforeEach(async function () { + await this.token.setApprovalForAll(operator, true, { from: owner }); + }); + + it('keeps the approval to the given address', async function () { + await this.token.setApprovalForAll(operator, true, { from: owner }); + + expect(await this.token.isApprovedForAll(owner, operator)).to.equal(true); + }); + + it('emits an approval event', async function () { + const receipt = await this.token.setApprovalForAll(operator, true, { from: owner }); + + expectEvent(receipt, 'ApprovalForAll', { + owner: owner, + operator: operator, + approved: true, + }); + }); + }); + }); + + context('when the operator is the owner', function () { + it('reverts', async function () { + await expectRevert(this.token.setApprovalForAll(owner, true, { from: owner }), + 'ERC721: approve to caller'); + }); + }); + }); + + describe('getApproved', async function () { + context('when token is not minted', async function () { + it('reverts', async function () { + await expectRevert( + this.token.getApproved(nonExistentTokenId), + 'ERC721: invalid token ID', + ); + }); + }); + + context('when token has been minted ', async function () { + it('should return the zero address', async function () { + expect(await this.token.getApproved(firstTokenId)).to.be.equal( + ZERO_ADDRESS, + ); + }); + + context('when account has been approved', async function () { + beforeEach(async function () { + await this.token.approve(approved, firstTokenId, { from: owner }); + }); + + it('returns approved account', async function () { + expect(await this.token.getApproved(firstTokenId)).to.be.equal(approved); + }); + }); + }); + }); + }); + + describe('_mint(address, uint256)', function () { + it('reverts with a null destination address', async function () { + await expectRevert( + this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address', + ); + }); + + context('with minted token', async function () { + beforeEach(async function () { + (this.receipt = await this.token.mint(owner, firstTokenId)); + }); + + it('emits a Transfer event', function () { + expectEvent(this.receipt, 'Transfer', { from: ZERO_ADDRESS, to: owner, tokenId: firstTokenId }); + }); + + it('creates the token', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1'); + expect(await this.token.ownerOf(firstTokenId)).to.equal(owner); + }); + + it('reverts when adding a token id that already exists', async function () { + await expectRevert(this.token.mint(owner, firstTokenId), 'ERC721: token already minted'); + }); + }); + }); + + describe('_burn', function () { + it('reverts when burning a non-existent token id', async function () { + await expectRevert( + this.token.burn(nonExistentTokenId), 'ERC721: invalid token ID', + ); + }); + + context('with minted tokens', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + await this.token.mint(owner, secondTokenId); + }); + + context('with burnt token', function () { + beforeEach(async function () { + (this.receipt = await this.token.burn(firstTokenId)); + }); + + it('emits a Transfer event', function () { + expectEvent(this.receipt, 'Transfer', { from: owner, to: ZERO_ADDRESS, tokenId: firstTokenId }); + }); + + it('deletes the token', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1'); + await expectRevert( + this.token.ownerOf(firstTokenId), 'ERC721: invalid token ID', + ); + }); + + it('reverts when burning a token id that has been deleted', async function () { + await expectRevert( + this.token.burn(firstTokenId), 'ERC721: invalid token ID', + ); + }); + }); + }); + }); +} + +function shouldBehaveLikeERC721Enumerable (errorPrefix, owner, newOwner, approved, anotherApproved, operator, other) { + shouldSupportInterfaces([ + 'ERC721Enumerable', + ]); + + context('with minted tokens', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + await this.token.mint(owner, secondTokenId); + this.toWhom = other; // default to other for toWhom in context-dependent tests + }); + + describe('totalSupply', function () { + it('returns total token supply', async function () { + expect(await this.token.totalSupply()).to.be.bignumber.equal('2'); + }); + }); + + describe('tokenOfOwnerByIndex', function () { + describe('when the given index is lower than the amount of tokens owned by the given address', function () { + it('returns the token ID placed at the given index', async function () { + expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(firstTokenId); + }); + }); + + describe('when the index is greater than or equal to the total tokens owned by the given address', function () { + it('reverts', async function () { + await expectRevert( + this.token.tokenOfOwnerByIndex(owner, 2), 'ERC721Enumerable: owner index out of bounds', + ); + }); + }); + + describe('when the given address does not own any token', function () { + it('reverts', async function () { + await expectRevert( + this.token.tokenOfOwnerByIndex(other, 0), 'ERC721Enumerable: owner index out of bounds', + ); + }); + }); + + describe('after transferring all tokens to another user', function () { + beforeEach(async function () { + await this.token.transferFrom(owner, other, firstTokenId, { from: owner }); + await this.token.transferFrom(owner, other, secondTokenId, { from: owner }); + }); + + it('returns correct token IDs for target', async function () { + expect(await this.token.balanceOf(other)).to.be.bignumber.equal('2'); + const tokensListed = await Promise.all( + [0, 1].map(i => this.token.tokenOfOwnerByIndex(other, i)), + ); + expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(), + secondTokenId.toNumber()]); + }); + + it('returns empty collection for original owner', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('0'); + await expectRevert( + this.token.tokenOfOwnerByIndex(owner, 0), 'ERC721Enumerable: owner index out of bounds', + ); + }); + }); + }); + + describe('tokenByIndex', function () { + it('returns all tokens', async function () { + const tokensListed = await Promise.all( + [0, 1].map(i => this.token.tokenByIndex(i)), + ); + expect(tokensListed.map(t => t.toNumber())).to.have.members([firstTokenId.toNumber(), + secondTokenId.toNumber()]); + }); + + it('reverts if index is greater than supply', async function () { + await expectRevert( + this.token.tokenByIndex(2), 'ERC721Enumerable: global index out of bounds', + ); + }); + + [firstTokenId, secondTokenId].forEach(function (tokenId) { + it(`returns all tokens after burning token ${tokenId} and minting new tokens`, async function () { + const newTokenId = new BN(300); + const anotherNewTokenId = new BN(400); + + await this.token.burn(tokenId); + await this.token.mint(newOwner, newTokenId); + await this.token.mint(newOwner, anotherNewTokenId); + + expect(await this.token.totalSupply()).to.be.bignumber.equal('3'); + + const tokensListed = await Promise.all( + [0, 1, 2].map(i => this.token.tokenByIndex(i)), + ); + const expectedTokens = [firstTokenId, secondTokenId, newTokenId, anotherNewTokenId].filter( + x => (x !== tokenId), + ); + expect(tokensListed.map(t => t.toNumber())).to.have.members(expectedTokens.map(t => t.toNumber())); + }); + }); + }); + }); + + describe('_mint(address, uint256)', function () { + it('reverts with a null destination address', async function () { + await expectRevert( + this.token.mint(ZERO_ADDRESS, firstTokenId), 'ERC721: mint to the zero address', + ); + }); + + context('with minted token', async function () { + beforeEach(async function () { + (this.receipt = await this.token.mint(owner, firstTokenId)); + }); + + it('adjusts owner tokens by index', async function () { + expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(firstTokenId); + }); + + it('adjusts all tokens list', async function () { + expect(await this.token.tokenByIndex(0)).to.be.bignumber.equal(firstTokenId); + }); + }); + }); + + describe('_burn', function () { + it('reverts when burning a non-existent token id', async function () { + await expectRevert( + this.token.burn(firstTokenId), 'ERC721: invalid token ID', + ); + }); + + context('with minted tokens', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + await this.token.mint(owner, secondTokenId); + }); + + context('with burnt token', function () { + beforeEach(async function () { + (this.receipt = await this.token.burn(firstTokenId)); + }); + + it('removes that token from the token list of the owner', async function () { + expect(await this.token.tokenOfOwnerByIndex(owner, 0)).to.be.bignumber.equal(secondTokenId); + }); + + it('adjusts all tokens list', async function () { + expect(await this.token.tokenByIndex(0)).to.be.bignumber.equal(secondTokenId); + }); + + it('burns all tokens', async function () { + await this.token.burn(secondTokenId, { from: owner }); + expect(await this.token.totalSupply()).to.be.bignumber.equal('0'); + await expectRevert( + this.token.tokenByIndex(0), 'ERC721Enumerable: global index out of bounds', + ); + }); + }); + }); + }); +} + +function shouldBehaveLikeERC721Metadata (errorPrefix, name, symbol, owner) { + shouldSupportInterfaces([ + 'ERC721Metadata', + ]); + + describe('metadata', function () { + it('has a name', async function () { + expect(await this.token.name()).to.be.equal(name); + }); + + it('has a symbol', async function () { + expect(await this.token.symbol()).to.be.equal(symbol); + }); + + describe('token URI', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + }); + + it('return empty string by default', async function () { + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(''); + }); + + it('reverts when queried for non existent token id', async function () { + await expectRevert( + this.token.tokenURI(nonExistentTokenId), 'ERC721: invalid token ID', + ); + }); + + describe('base URI', function () { + beforeEach(function () { + if (this.token.setBaseURI === undefined) { + this.skip(); + } + }); + + it('base URI can be set', async function () { + await this.token.setBaseURI(baseURI); + expect(await this.token.baseURI()).to.equal(baseURI); + }); + + it('base URI is added as a prefix to the token URI', async function () { + await this.token.setBaseURI(baseURI); + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + firstTokenId.toString()); + }); + + it('token URI can be changed by changing the base URI', async function () { + await this.token.setBaseURI(baseURI); + const newBaseURI = 'https://api.example.com/v2/'; + await this.token.setBaseURI(newBaseURI); + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(newBaseURI + firstTokenId.toString()); + }); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC721, + shouldBehaveLikeERC721Enumerable, + shouldBehaveLikeERC721Metadata, +}; diff --git a/lib/openzeppelin-contracts/test/token/ERC721/ERC721.test.js b/lib/openzeppelin-contracts/test/token/ERC721/ERC721.test.js new file mode 100644 index 0000000..1abbd66 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/ERC721.test.js @@ -0,0 +1,18 @@ +const { + shouldBehaveLikeERC721, + shouldBehaveLikeERC721Metadata, +} = require('./ERC721.behavior'); + +const ERC721Mock = artifacts.require('ERC721Mock'); + +contract('ERC721', function (accounts) { + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + + beforeEach(async function () { + this.token = await ERC721Mock.new(name, symbol); + }); + + shouldBehaveLikeERC721('ERC721', ...accounts); + shouldBehaveLikeERC721Metadata('ERC721', name, symbol, ...accounts); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/ERC721Enumerable.test.js b/lib/openzeppelin-contracts/test/token/ERC721/ERC721Enumerable.test.js new file mode 100644 index 0000000..2c13621 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/ERC721Enumerable.test.js @@ -0,0 +1,20 @@ +const { + shouldBehaveLikeERC721, + shouldBehaveLikeERC721Metadata, + shouldBehaveLikeERC721Enumerable, +} = require('./ERC721.behavior'); + +const ERC721Mock = artifacts.require('ERC721EnumerableMock'); + +contract('ERC721Enumerable', function (accounts) { + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + + beforeEach(async function () { + this.token = await ERC721Mock.new(name, symbol); + }); + + shouldBehaveLikeERC721('ERC721', ...accounts); + shouldBehaveLikeERC721Metadata('ERC721', name, symbol, ...accounts); + shouldBehaveLikeERC721Enumerable('ERC721', ...accounts); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Burnable.test.js b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Burnable.test.js new file mode 100644 index 0000000..e8fc334 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Burnable.test.js @@ -0,0 +1,78 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const ERC721BurnableMock = artifacts.require('ERC721BurnableMock'); + +contract('ERC721Burnable', function (accounts) { + const [owner, approved] = accounts; + + const firstTokenId = new BN(1); + const secondTokenId = new BN(2); + const unknownTokenId = new BN(3); + + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + + beforeEach(async function () { + this.token = await ERC721BurnableMock.new(name, symbol); + }); + + describe('like a burnable ERC721', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + await this.token.mint(owner, secondTokenId); + }); + + describe('burn', function () { + const tokenId = firstTokenId; + let receipt = null; + + describe('when successful', function () { + beforeEach(async function () { + receipt = await this.token.burn(tokenId, { from: owner }); + }); + + it('burns the given token ID and adjusts the balance of the owner', async function () { + await expectRevert( + this.token.ownerOf(tokenId), + 'ERC721: invalid token ID', + ); + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal('1'); + }); + + it('emits a burn event', async function () { + expectEvent(receipt, 'Transfer', { + from: owner, + to: ZERO_ADDRESS, + tokenId: tokenId, + }); + }); + }); + + describe('when there is a previous approval burned', function () { + beforeEach(async function () { + await this.token.approve(approved, tokenId, { from: owner }); + receipt = await this.token.burn(tokenId, { from: owner }); + }); + + context('getApproved', function () { + it('reverts', async function () { + await expectRevert( + this.token.getApproved(tokenId), 'ERC721: invalid token ID', + ); + }); + }); + }); + + describe('when the given token ID was not tracked by this contract', function () { + it('reverts', async function () { + await expectRevert( + this.token.burn(unknownTokenId, { from: owner }), 'ERC721: invalid token ID', + ); + }); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Consecutive.test.js b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Consecutive.test.js new file mode 100644 index 0000000..5e480ab --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Consecutive.test.js @@ -0,0 +1,224 @@ +const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const ERC721ConsecutiveMock = artifacts.require('ERC721ConsecutiveMock'); +const ERC721ConsecutiveEnumerableMock = artifacts.require('ERC721ConsecutiveEnumerableMock'); +const ERC721ConsecutiveNoConstructorMintMock = artifacts.require('ERC721ConsecutiveNoConstructorMintMock'); + +contract('ERC721Consecutive', function (accounts) { + const [ user1, user2, user3, receiver ] = accounts; + + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + const batches = [ + { receiver: user1, amount: 0 }, + { receiver: user1, amount: 3 }, + { receiver: user2, amount: 5 }, + { receiver: user3, amount: 0 }, + { receiver: user1, amount: 7 }, + ]; + const delegates = [ user1, user3 ]; + + describe('with valid batches', function () { + beforeEach(async function () { + this.token = await ERC721ConsecutiveMock.new( + name, + symbol, + delegates, + batches.map(({ receiver }) => receiver), + batches.map(({ amount }) => amount), + ); + }); + + describe('minting during construction', function () { + it('events are emitted at construction', async function () { + let first = 0; + + for (const batch of batches) { + if (batch.amount > 0) { + await expectEvent.inConstruction(this.token, 'ConsecutiveTransfer', { + fromTokenId: web3.utils.toBN(first), + toTokenId: web3.utils.toBN(first + batch.amount - 1), + fromAddress: constants.ZERO_ADDRESS, + toAddress: batch.receiver, + }); + } else { + // expectEvent.notEmitted.inConstruction only looks at event name, and doesn't check the parameters + } + first += batch.amount; + } + }); + + it('ownership is set', async function () { + const owners = batches.flatMap(({ receiver, amount }) => Array(amount).fill(receiver)); + + for (const tokenId in owners) { + expect(await this.token.ownerOf(tokenId)) + .to.be.equal(owners[tokenId]); + } + }); + + it('balance & voting power are set', async function () { + for (const account of accounts) { + const balance = batches + .filter(({ receiver }) => receiver === account) + .map(({ amount }) => amount) + .reduce((a, b) => a + b, 0); + + expect(await this.token.balanceOf(account)) + .to.be.bignumber.equal(web3.utils.toBN(balance)); + + // If not delegated at construction, check before + do delegation + if (!delegates.includes(account)) { + expect(await this.token.getVotes(account)) + .to.be.bignumber.equal(web3.utils.toBN(0)); + + await this.token.delegate(account, { from: account }); + } + + // At this point all accounts should have delegated + expect(await this.token.getVotes(account)) + .to.be.bignumber.equal(web3.utils.toBN(balance)); + } + }); + }); + + describe('minting after construction', function () { + it('consecutive minting is not possible after construction', async function () { + await expectRevert( + this.token.mintConsecutive(user1, 10), + 'ERC721Consecutive: batch minting restricted to constructor', + ); + }); + + it('simple minting is possible after construction', async function () { + const tokenId = batches.reduce((acc, { amount }) => acc + amount, 0); + + expect(await this.token.exists(tokenId)).to.be.equal(false); + + expectEvent( + await this.token.mint(user1, tokenId), + 'Transfer', + { from: constants.ZERO_ADDRESS, to: user1, tokenId: tokenId.toString() }, + ); + }); + + it('cannot mint a token that has been batched minted', async function () { + const tokenId = batches.reduce((acc, { amount }) => acc + amount, 0) - 1; + + expect(await this.token.exists(tokenId)).to.be.equal(true); + + await expectRevert( + this.token.mint(user1, tokenId), + 'ERC721: token already minted', + ); + }); + }); + + describe('ERC721 behavior', function () { + it('core takes over ownership on transfer', async function () { + await this.token.transferFrom(user1, receiver, 1, { from: user1 }); + + expect(await this.token.ownerOf(1)).to.be.equal(receiver); + }); + + it('tokens can be burned and re-minted #1', async function () { + expectEvent( + await this.token.burn(1, { from: user1 }), + 'Transfer', + { from: user1, to: constants.ZERO_ADDRESS, tokenId: '1' }, + ); + + await expectRevert(this.token.ownerOf(1), 'ERC721: invalid token ID'); + + expectEvent( + await this.token.mint(user2, 1), + 'Transfer', + { from: constants.ZERO_ADDRESS, to: user2, tokenId: '1' }, + ); + + expect(await this.token.ownerOf(1)).to.be.equal(user2); + }); + + it('tokens can be burned and re-minted #2', async function () { + const tokenId = batches.reduce((acc, { amount }) => acc.addn(amount), web3.utils.toBN(0)); + + expect(await this.token.exists(tokenId)).to.be.equal(false); + await expectRevert(this.token.ownerOf(tokenId), 'ERC721: invalid token ID'); + + // mint + await this.token.mint(user1, tokenId); + + expect(await this.token.exists(tokenId)).to.be.equal(true); + expect(await this.token.ownerOf(tokenId), user1); + + // burn + expectEvent( + await this.token.burn(tokenId, { from: user1 }), + 'Transfer', + { from: user1, to: constants.ZERO_ADDRESS, tokenId }, + ); + + expect(await this.token.exists(tokenId)).to.be.equal(false); + await expectRevert(this.token.ownerOf(tokenId), 'ERC721: invalid token ID'); + + // re-mint + expectEvent( + await this.token.mint(user2, tokenId), + 'Transfer', + { from: constants.ZERO_ADDRESS, to: user2, tokenId }, + ); + + expect(await this.token.exists(tokenId)).to.be.equal(true); + expect(await this.token.ownerOf(tokenId), user2); + }); + }); + }); + + describe('invalid use', function () { + it('cannot mint a batch larger than 5000', async function () { + await expectRevert( + ERC721ConsecutiveMock.new( + name, + symbol, + [], + [user1], + ['5001'], + ), + 'ERC721Consecutive: batch too large', + ); + }); + + it('cannot use single minting during construction', async function () { + await expectRevert( + ERC721ConsecutiveNoConstructorMintMock.new( + name, + symbol, + ), + 'ERC721Consecutive: can\'t mint during construction', + ); + }); + + it('cannot use single minting during construction', async function () { + await expectRevert( + ERC721ConsecutiveNoConstructorMintMock.new( + name, + symbol, + ), + 'ERC721Consecutive: can\'t mint during construction', + ); + }); + + it('consecutive mint not compatible with enumerability', async function () { + await expectRevert( + ERC721ConsecutiveEnumerableMock.new( + name, + symbol, + batches.map(({ receiver }) => receiver), + batches.map(({ amount }) => amount), + ), + 'ERC721Enumerable: consecutive transfers not supported', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Pausable.test.js b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Pausable.test.js new file mode 100644 index 0000000..16847dc --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Pausable.test.js @@ -0,0 +1,98 @@ +const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const ERC721PausableMock = artifacts.require('ERC721PausableMock'); + +contract('ERC721Pausable', function (accounts) { + const [ owner, receiver, operator ] = accounts; + + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + + beforeEach(async function () { + this.token = await ERC721PausableMock.new(name, symbol); + }); + + context('when token is paused', function () { + const firstTokenId = new BN(1); + const secondTokenId = new BN(1337); + + const mockData = '0x42'; + + beforeEach(async function () { + await this.token.mint(owner, firstTokenId, { from: owner }); + await this.token.pause(); + }); + + it('reverts when trying to transferFrom', async function () { + await expectRevert( + this.token.transferFrom(owner, receiver, firstTokenId, { from: owner }), + 'ERC721Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to safeTransferFrom', async function () { + await expectRevert( + this.token.safeTransferFrom(owner, receiver, firstTokenId, { from: owner }), + 'ERC721Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to safeTransferFrom with data', async function () { + await expectRevert( + this.token.methods['safeTransferFrom(address,address,uint256,bytes)']( + owner, receiver, firstTokenId, mockData, { from: owner }, + ), 'ERC721Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to mint', async function () { + await expectRevert( + this.token.mint(receiver, secondTokenId), + 'ERC721Pausable: token transfer while paused', + ); + }); + + it('reverts when trying to burn', async function () { + await expectRevert( + this.token.burn(firstTokenId), + 'ERC721Pausable: token transfer while paused', + ); + }); + + describe('getApproved', function () { + it('returns approved address', async function () { + const approvedAccount = await this.token.getApproved(firstTokenId); + expect(approvedAccount).to.equal(ZERO_ADDRESS); + }); + }); + + describe('balanceOf', function () { + it('returns the amount of tokens owned by the given address', async function () { + const balance = await this.token.balanceOf(owner); + expect(balance).to.be.bignumber.equal('1'); + }); + }); + + describe('ownerOf', function () { + it('returns the amount of tokens owned by the given address', async function () { + const ownerOfToken = await this.token.ownerOf(firstTokenId); + expect(ownerOfToken).to.equal(owner); + }); + }); + + describe('exists', function () { + it('returns token existence', async function () { + expect(await this.token.exists(firstTokenId)).to.equal(true); + }); + }); + + describe('isApprovedForAll', function () { + it('returns the approval of the operator', async function () { + expect(await this.token.isApprovedForAll(owner, operator)).to.equal(false); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Royalty.test.js b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Royalty.test.js new file mode 100644 index 0000000..29b28dd --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Royalty.test.js @@ -0,0 +1,40 @@ +const { BN, constants } = require('@openzeppelin/test-helpers'); +const ERC721RoyaltyMock = artifacts.require('ERC721RoyaltyMock'); +const { ZERO_ADDRESS } = constants; + +const { shouldBehaveLikeERC2981 } = require('../../common/ERC2981.behavior'); + +contract('ERC721Royalty', function (accounts) { + const [ account1, account2 ] = accounts; + const tokenId1 = new BN('1'); + const tokenId2 = new BN('2'); + const royalty = new BN('200'); + const salePrice = new BN('1000'); + + beforeEach(async function () { + this.token = await ERC721RoyaltyMock.new('My Token', 'TKN'); + + await this.token.mint(account1, tokenId1); + await this.token.mint(account1, tokenId2); + this.account1 = account1; + this.account2 = account2; + this.tokenId1 = tokenId1; + this.tokenId2 = tokenId2; + this.salePrice = salePrice; + }); + + describe('token specific functions', function () { + beforeEach(async function () { + await this.token.setTokenRoyalty(tokenId1, account1, royalty); + }); + + it('removes royalty information after burn', async function () { + await this.token.burn(tokenId1); + const tokenInfo = await this.token.royaltyInfo(tokenId1, salePrice); + + expect(tokenInfo[0]).to.be.equal(ZERO_ADDRESS); + expect(tokenInfo[1]).to.be.bignumber.equal(new BN('0')); + }); + }); + shouldBehaveLikeERC2981(); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721URIStorage.test.js b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721URIStorage.test.js new file mode 100644 index 0000000..eeacf5e --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721URIStorage.test.js @@ -0,0 +1,96 @@ +const { BN, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC721URIStorageMock = artifacts.require('ERC721URIStorageMock'); + +contract('ERC721URIStorage', function (accounts) { + const [ owner ] = accounts; + + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + + const firstTokenId = new BN('5042'); + const nonExistentTokenId = new BN('13'); + + beforeEach(async function () { + this.token = await ERC721URIStorageMock.new(name, symbol); + }); + + describe('token URI', function () { + beforeEach(async function () { + await this.token.mint(owner, firstTokenId); + }); + + const baseURI = 'https://api.example.com/v1/'; + const sampleUri = 'mock://mytoken'; + + it('it is empty by default', async function () { + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(''); + }); + + it('reverts when queried for non existent token id', async function () { + await expectRevert( + this.token.tokenURI(nonExistentTokenId), 'ERC721: invalid token ID', + ); + }); + + it('can be set for a token id', async function () { + await this.token.setTokenURI(firstTokenId, sampleUri); + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(sampleUri); + }); + + it('reverts when setting for non existent token id', async function () { + await expectRevert( + this.token.setTokenURI(nonExistentTokenId, sampleUri), 'ERC721URIStorage: URI set of nonexistent token', + ); + }); + + it('base URI can be set', async function () { + await this.token.setBaseURI(baseURI); + expect(await this.token.baseURI()).to.equal(baseURI); + }); + + it('base URI is added as a prefix to the token URI', async function () { + await this.token.setBaseURI(baseURI); + await this.token.setTokenURI(firstTokenId, sampleUri); + + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + sampleUri); + }); + + it('token URI can be changed by changing the base URI', async function () { + await this.token.setBaseURI(baseURI); + await this.token.setTokenURI(firstTokenId, sampleUri); + + const newBaseURI = 'https://api.example.com/v2/'; + await this.token.setBaseURI(newBaseURI); + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(newBaseURI + sampleUri); + }); + + it('tokenId is appended to base URI for tokens with no URI', async function () { + await this.token.setBaseURI(baseURI); + + expect(await this.token.tokenURI(firstTokenId)).to.be.equal(baseURI + firstTokenId); + }); + + it('tokens without URI can be burnt ', async function () { + await this.token.burn(firstTokenId, { from: owner }); + + expect(await this.token.exists(firstTokenId)).to.equal(false); + await expectRevert( + this.token.tokenURI(firstTokenId), 'ERC721: invalid token ID', + ); + }); + + it('tokens with URI can be burnt ', async function () { + await this.token.setTokenURI(firstTokenId, sampleUri); + + await this.token.burn(firstTokenId, { from: owner }); + + expect(await this.token.exists(firstTokenId)).to.equal(false); + await expectRevert( + this.token.tokenURI(firstTokenId), 'ERC721: invalid token ID', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Votes.test.js b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Votes.test.js new file mode 100644 index 0000000..6f001f2 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/extensions/ERC721Votes.test.js @@ -0,0 +1,174 @@ +/* eslint-disable */ + +const { BN, expectEvent, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const { promisify } = require('util'); +const queue = promisify(setImmediate); + +const ERC721VotesMock = artifacts.require('ERC721VotesMock'); + +const { shouldBehaveLikeVotes } = require('../../../governance/utils/Votes.behavior'); + +contract('ERC721Votes', function (accounts) { + const [ account1, account2, account1Delegatee, other1, other2 ] = accounts; + this.name = 'My Vote'; + const symbol = 'MTKN'; + + beforeEach(async function () { + this.votes = await ERC721VotesMock.new(name, symbol); + + // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id + // from within the EVM as from the JSON RPC interface. + // See https://github.com/trufflesuite/ganache-core/issues/515 + this.chainId = await this.votes.getChainId(); + + this.NFT0 = new BN('10000000000000000000000000'); + this.NFT1 = new BN('10'); + this.NFT2 = new BN('20'); + this.NFT3 = new BN('30'); + }); + + describe('balanceOf', function () { + beforeEach(async function () { + await this.votes.mint(account1, this.NFT0); + await this.votes.mint(account1, this.NFT1); + await this.votes.mint(account1, this.NFT2); + await this.votes.mint(account1, this.NFT3); + }); + + it('grants to initial account', async function () { + expect(await this.votes.balanceOf(account1)).to.be.bignumber.equal('4'); + }); + }); + + describe('transfers', function () { + beforeEach(async function () { + await this.votes.mint(account1, this.NFT0); + }); + + it('no delegation', async function () { + const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 }); + expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 }); + expectEvent.notEmitted(receipt, 'DelegateVotesChanged'); + + this.account1Votes = '0'; + this.account2Votes = '0'; + }); + + it('sender delegation', async function () { + await this.votes.delegate(account1, { from: account1 }); + + const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 }); + expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: account1, previousBalance: '1', newBalance: '0' }); + + const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer'); + expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true); + + this.account1Votes = '0'; + this.account2Votes = '0'; + }); + + it('receiver delegation', async function () { + await this.votes.delegate(account2, { from: account2 }); + + const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 }); + expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: account2, previousBalance: '0', newBalance: '1' }); + + const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer'); + expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true); + + this.account1Votes = '0'; + this.account2Votes = '1'; + }); + + it('full delegation', async function () { + await this.votes.delegate(account1, { from: account1 }); + await this.votes.delegate(account2, { from: account2 }); + + const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 }); + expectEvent(receipt, 'Transfer', { from: account1, to: account2, tokenId: this.NFT0 }); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: account1, previousBalance: '1', newBalance: '0'}); + expectEvent(receipt, 'DelegateVotesChanged', { delegate: account2, previousBalance: '0', newBalance: '1' }); + + const { logIndex: transferLogIndex } = receipt.logs.find(({ event }) => event == 'Transfer'); + expect(receipt.logs.filter(({ event }) => event == 'DelegateVotesChanged').every(({ logIndex }) => transferLogIndex < logIndex)).to.be.equal(true); + + this.account1Votes = '0'; + this.account2Votes = '1'; + }); + + it('returns the same total supply on transfers', async function () { + await this.votes.delegate(account1, { from: account1 }); + + const { receipt } = await this.votes.transferFrom(account1, account2, this.NFT0, { from: account1 }); + + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.votes.getPastTotalSupply(receipt.blockNumber - 1)).to.be.bignumber.equal('1'); + expect(await this.votes.getPastTotalSupply(receipt.blockNumber + 1)).to.be.bignumber.equal('1'); + + this.account1Votes = '0'; + this.account2Votes = '0'; + }); + + it('generally returns the voting balance at the appropriate checkpoint', async function () { + await this.votes.mint(account1, this.NFT1); + await this.votes.mint(account1, this.NFT2); + await this.votes.mint(account1, this.NFT3); + + const total = await this.votes.balanceOf(account1); + + const t1 = await this.votes.delegate(other1, { from: account1 }); + await time.advanceBlock(); + await time.advanceBlock(); + const t2 = await this.votes.transferFrom(account1, other2, this.NFT0, { from: account1 }); + await time.advanceBlock(); + await time.advanceBlock(); + const t3 = await this.votes.transferFrom(account1, other2, this.NFT2, { from: account1 }); + await time.advanceBlock(); + await time.advanceBlock(); + const t4 = await this.votes.transferFrom(other2, account1, this.NFT2, { from: other2 }); + await time.advanceBlock(); + await time.advanceBlock(); + + expect(await this.votes.getPastVotes(other1, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.votes.getPastVotes(other1, t1.receipt.blockNumber)).to.be.bignumber.equal(total); + expect(await this.votes.getPastVotes(other1, t1.receipt.blockNumber + 1)).to.be.bignumber.equal(total); + expect(await this.votes.getPastVotes(other1, t2.receipt.blockNumber)).to.be.bignumber.equal('3'); + expect(await this.votes.getPastVotes(other1, t2.receipt.blockNumber + 1)).to.be.bignumber.equal('3'); + expect(await this.votes.getPastVotes(other1, t3.receipt.blockNumber)).to.be.bignumber.equal('2'); + expect(await this.votes.getPastVotes(other1, t3.receipt.blockNumber + 1)).to.be.bignumber.equal('2'); + expect(await this.votes.getPastVotes(other1, t4.receipt.blockNumber)).to.be.bignumber.equal('3'); + expect(await this.votes.getPastVotes(other1, t4.receipt.blockNumber + 1)).to.be.bignumber.equal('3'); + + this.account1Votes = '0'; + this.account2Votes = '0'; + }); + + afterEach(async function () { + expect(await this.votes.getVotes(account1)).to.be.bignumber.equal(this.account1Votes); + expect(await this.votes.getVotes(account2)).to.be.bignumber.equal(this.account2Votes); + + // need to advance 2 blocks to see the effect of a transfer on "getPastVotes" + const blockNumber = await time.latestBlock(); + await time.advanceBlock(); + expect(await this.votes.getPastVotes(account1, blockNumber)).to.be.bignumber.equal(this.account1Votes); + expect(await this.votes.getPastVotes(account2, blockNumber)).to.be.bignumber.equal(this.account2Votes); + }); + }); + + describe('Voting workflow', function () { + beforeEach(async function () { + this.account1 = account1; + this.account1Delegatee = account1Delegatee; + this.account2 = account2; + this.name = 'My Vote'; + }); + + shouldBehaveLikeVotes(); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/presets/ERC721PresetMinterPauserAutoId.test.js b/lib/openzeppelin-contracts/test/token/ERC721/presets/ERC721PresetMinterPauserAutoId.test.js new file mode 100644 index 0000000..4ad7355 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/presets/ERC721PresetMinterPauserAutoId.test.js @@ -0,0 +1,125 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; +const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior'); + +const { expect } = require('chai'); + +const ERC721PresetMinterPauserAutoId = artifacts.require('ERC721PresetMinterPauserAutoId'); + +contract('ERC721PresetMinterPauserAutoId', function (accounts) { + const [ deployer, other ] = accounts; + + const name = 'MinterAutoIDToken'; + const symbol = 'MAIT'; + const baseURI = 'my.app/'; + + const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const MINTER_ROLE = web3.utils.soliditySha3('MINTER_ROLE'); + + beforeEach(async function () { + this.token = await ERC721PresetMinterPauserAutoId.new(name, symbol, baseURI, { from: deployer }); + }); + + shouldSupportInterfaces(['ERC721', 'ERC721Enumerable', 'AccessControl', 'AccessControlEnumerable']); + + it('token has correct name', async function () { + expect(await this.token.name()).to.equal(name); + }); + + it('token has correct symbol', async function () { + expect(await this.token.symbol()).to.equal(symbol); + }); + + it('deployer has the default admin role', async function () { + expect(await this.token.getRoleMemberCount(DEFAULT_ADMIN_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(DEFAULT_ADMIN_ROLE, 0)).to.equal(deployer); + }); + + it('deployer has the minter role', async function () { + expect(await this.token.getRoleMemberCount(MINTER_ROLE)).to.be.bignumber.equal('1'); + expect(await this.token.getRoleMember(MINTER_ROLE, 0)).to.equal(deployer); + }); + + it('minter role admin is the default admin', async function () { + expect(await this.token.getRoleAdmin(MINTER_ROLE)).to.equal(DEFAULT_ADMIN_ROLE); + }); + + describe('minting', function () { + it('deployer can mint tokens', async function () { + const tokenId = new BN('0'); + + const receipt = await this.token.mint(other, { from: deployer }); + expectEvent(receipt, 'Transfer', { from: ZERO_ADDRESS, to: other, tokenId }); + + expect(await this.token.balanceOf(other)).to.be.bignumber.equal('1'); + expect(await this.token.ownerOf(tokenId)).to.equal(other); + + expect(await this.token.tokenURI(tokenId)).to.equal(baseURI + tokenId); + }); + + it('other accounts cannot mint tokens', async function () { + await expectRevert( + this.token.mint(other, { from: other }), + 'ERC721PresetMinterPauserAutoId: must have minter role to mint', + ); + }); + }); + + describe('pausing', function () { + it('deployer can pause', async function () { + const receipt = await this.token.pause({ from: deployer }); + expectEvent(receipt, 'Paused', { account: deployer }); + + expect(await this.token.paused()).to.equal(true); + }); + + it('deployer can unpause', async function () { + await this.token.pause({ from: deployer }); + + const receipt = await this.token.unpause({ from: deployer }); + expectEvent(receipt, 'Unpaused', { account: deployer }); + + expect(await this.token.paused()).to.equal(false); + }); + + it('cannot mint while paused', async function () { + await this.token.pause({ from: deployer }); + + await expectRevert( + this.token.mint(other, { from: deployer }), + 'ERC721Pausable: token transfer while paused', + ); + }); + + it('other accounts cannot pause', async function () { + await expectRevert( + this.token.pause({ from: other }), + 'ERC721PresetMinterPauserAutoId: must have pauser role to pause', + ); + }); + + it('other accounts cannot unpause', async function () { + await this.token.pause({ from: deployer }); + + await expectRevert( + this.token.unpause({ from: other }), + 'ERC721PresetMinterPauserAutoId: must have pauser role to unpause', + ); + }); + }); + + describe('burning', function () { + it('holders can burn their tokens', async function () { + const tokenId = new BN('0'); + + await this.token.mint(other, { from: deployer }); + + const receipt = await this.token.burn(tokenId, { from: other }); + + expectEvent(receipt, 'Transfer', { from: other, to: ZERO_ADDRESS, tokenId }); + + expect(await this.token.balanceOf(other)).to.be.bignumber.equal('0'); + expect(await this.token.totalSupply()).to.be.bignumber.equal('0'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC721/utils/ERC721Holder.test.js b/lib/openzeppelin-contracts/test/token/ERC721/utils/ERC721Holder.test.js new file mode 100644 index 0000000..2431e66 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC721/utils/ERC721Holder.test.js @@ -0,0 +1,24 @@ +const { BN } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC721Holder = artifacts.require('ERC721Holder'); +const ERC721Mock = artifacts.require('ERC721Mock'); + +contract('ERC721Holder', function (accounts) { + const [ owner ] = accounts; + + const name = 'Non Fungible Token'; + const symbol = 'NFT'; + + it('receives an ERC721 token', async function () { + const token = await ERC721Mock.new(name, symbol); + const tokenId = new BN(1); + await token.mint(owner, tokenId); + + const receiver = await ERC721Holder.new(); + await token.safeTransferFrom(owner, receiver.address, tokenId, { from: owner }); + + expect(await token.ownerOf(tokenId)).to.be.equal(receiver.address); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC777/ERC777.behavior.js b/lib/openzeppelin-contracts/test/token/ERC777/ERC777.behavior.js new file mode 100644 index 0000000..f6af942 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC777/ERC777.behavior.js @@ -0,0 +1,555 @@ +const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock'); + +function shouldBehaveLikeERC777DirectSendBurn (holder, recipient, data) { + shouldBehaveLikeERC777DirectSend(holder, recipient, data); + shouldBehaveLikeERC777DirectBurn(holder, data); +} + +function shouldBehaveLikeERC777OperatorSendBurn (holder, recipient, operator, data, operatorData) { + shouldBehaveLikeERC777OperatorSend(holder, recipient, operator, data, operatorData); + shouldBehaveLikeERC777OperatorBurn(holder, operator, data, operatorData); +} + +function shouldBehaveLikeERC777UnauthorizedOperatorSendBurn (holder, recipient, operator, data, operatorData) { + shouldBehaveLikeERC777UnauthorizedOperatorSend(holder, recipient, operator, data, operatorData); + shouldBehaveLikeERC777UnauthorizedOperatorBurn(holder, operator, data, operatorData); +} + +function shouldBehaveLikeERC777DirectSend (holder, recipient, data) { + describe('direct send', function () { + context('when the sender has tokens', function () { + shouldDirectSendTokens(holder, recipient, new BN('0'), data); + shouldDirectSendTokens(holder, recipient, new BN('1'), data); + + it('reverts when sending more than the balance', async function () { + const balance = await this.token.balanceOf(holder); + await expectRevert.unspecified(this.token.send(recipient, balance.addn(1), data, { from: holder })); + }); + + it('reverts when sending to the zero address', async function () { + await expectRevert.unspecified(this.token.send(ZERO_ADDRESS, new BN('1'), data, { from: holder })); + }); + }); + + context('when the sender has no tokens', function () { + removeBalance(holder); + + shouldDirectSendTokens(holder, recipient, new BN('0'), data); + + it('reverts when sending a non-zero amount', async function () { + await expectRevert.unspecified(this.token.send(recipient, new BN('1'), data, { from: holder })); + }); + }); + }); +} + +function shouldBehaveLikeERC777OperatorSend (holder, recipient, operator, data, operatorData) { + describe('operator send', function () { + context('when the sender has tokens', async function () { + shouldOperatorSendTokens(holder, operator, recipient, new BN('0'), data, operatorData); + shouldOperatorSendTokens(holder, operator, recipient, new BN('1'), data, operatorData); + + it('reverts when sending more than the balance', async function () { + const balance = await this.token.balanceOf(holder); + await expectRevert.unspecified( + this.token.operatorSend(holder, recipient, balance.addn(1), data, operatorData, { from: operator }), + ); + }); + + it('reverts when sending to the zero address', async function () { + await expectRevert.unspecified( + this.token.operatorSend( + holder, ZERO_ADDRESS, new BN('1'), data, operatorData, { from: operator }, + ), + ); + }); + }); + + context('when the sender has no tokens', function () { + removeBalance(holder); + + shouldOperatorSendTokens(holder, operator, recipient, new BN('0'), data, operatorData); + + it('reverts when sending a non-zero amount', async function () { + await expectRevert.unspecified( + this.token.operatorSend(holder, recipient, new BN('1'), data, operatorData, { from: operator }), + ); + }); + + it('reverts when sending from the zero address', async function () { + // This is not yet reflected in the spec + await expectRevert.unspecified( + this.token.operatorSend( + ZERO_ADDRESS, recipient, new BN('0'), data, operatorData, { from: operator }, + ), + ); + }); + }); + }); +} + +function shouldBehaveLikeERC777UnauthorizedOperatorSend (holder, recipient, operator, data, operatorData) { + describe('operator send', function () { + it('reverts', async function () { + await expectRevert.unspecified(this.token.operatorSend(holder, recipient, new BN('0'), data, operatorData)); + }); + }); +} + +function shouldBehaveLikeERC777DirectBurn (holder, data) { + describe('direct burn', function () { + context('when the sender has tokens', function () { + shouldDirectBurnTokens(holder, new BN('0'), data); + shouldDirectBurnTokens(holder, new BN('1'), data); + + it('reverts when burning more than the balance', async function () { + const balance = await this.token.balanceOf(holder); + await expectRevert.unspecified(this.token.burn(balance.addn(1), data, { from: holder })); + }); + }); + + context('when the sender has no tokens', function () { + removeBalance(holder); + + shouldDirectBurnTokens(holder, new BN('0'), data); + + it('reverts when burning a non-zero amount', async function () { + await expectRevert.unspecified(this.token.burn(new BN('1'), data, { from: holder })); + }); + }); + }); +} + +function shouldBehaveLikeERC777OperatorBurn (holder, operator, data, operatorData) { + describe('operator burn', function () { + context('when the sender has tokens', async function () { + shouldOperatorBurnTokens(holder, operator, new BN('0'), data, operatorData); + shouldOperatorBurnTokens(holder, operator, new BN('1'), data, operatorData); + + it('reverts when burning more than the balance', async function () { + const balance = await this.token.balanceOf(holder); + await expectRevert.unspecified( + this.token.operatorBurn(holder, balance.addn(1), data, operatorData, { from: operator }), + ); + }); + }); + + context('when the sender has no tokens', function () { + removeBalance(holder); + + shouldOperatorBurnTokens(holder, operator, new BN('0'), data, operatorData); + + it('reverts when burning a non-zero amount', async function () { + await expectRevert.unspecified( + this.token.operatorBurn(holder, new BN('1'), data, operatorData, { from: operator }), + ); + }); + + it('reverts when burning from the zero address', async function () { + // This is not yet reflected in the spec + await expectRevert.unspecified( + this.token.operatorBurn( + ZERO_ADDRESS, new BN('0'), data, operatorData, { from: operator }, + ), + ); + }); + }); + }); +} + +function shouldBehaveLikeERC777UnauthorizedOperatorBurn (holder, operator, data, operatorData) { + describe('operator burn', function () { + it('reverts', async function () { + await expectRevert.unspecified(this.token.operatorBurn(holder, new BN('0'), data, operatorData)); + }); + }); +} + +function shouldDirectSendTokens (from, to, amount, data) { + shouldSendTokens(from, null, to, amount, data, null); +} + +function shouldOperatorSendTokens (from, operator, to, amount, data, operatorData) { + shouldSendTokens(from, operator, to, amount, data, operatorData); +} + +function shouldSendTokens (from, operator, to, amount, data, operatorData) { + const operatorCall = operator !== null; + + it(`${operatorCall ? 'operator ' : ''}can send an amount of ${amount}`, async function () { + const initialTotalSupply = await this.token.totalSupply(); + const initialFromBalance = await this.token.balanceOf(from); + const initialToBalance = await this.token.balanceOf(to); + + let receipt; + if (!operatorCall) { + (receipt = await this.token.send(to, amount, data, { from })); + expectEvent(receipt, 'Sent', { + operator: from, + from, + to, + amount, + data, + operatorData: null, + }); + } else { + (receipt = await this.token.operatorSend(from, to, amount, data, operatorData, { from: operator })); + expectEvent(receipt, 'Sent', { + operator, + from, + to, + amount, + data, + operatorData, + }); + } + + expectEvent(receipt, 'Transfer', { + from, + to, + value: amount, + }); + + const finalTotalSupply = await this.token.totalSupply(); + const finalFromBalance = await this.token.balanceOf(from); + const finalToBalance = await this.token.balanceOf(to); + + expect(finalTotalSupply).to.be.bignumber.equal(initialTotalSupply); + expect(finalToBalance.sub(initialToBalance)).to.be.bignumber.equal(amount); + expect(finalFromBalance.sub(initialFromBalance)).to.be.bignumber.equal(amount.neg()); + }); +} + +function shouldDirectBurnTokens (from, amount, data) { + shouldBurnTokens(from, null, amount, data, null); +} + +function shouldOperatorBurnTokens (from, operator, amount, data, operatorData) { + shouldBurnTokens(from, operator, amount, data, operatorData); +} + +function shouldBurnTokens (from, operator, amount, data, operatorData) { + const operatorCall = operator !== null; + + it(`${operatorCall ? 'operator ' : ''}can burn an amount of ${amount}`, async function () { + const initialTotalSupply = await this.token.totalSupply(); + const initialFromBalance = await this.token.balanceOf(from); + + let receipt; + if (!operatorCall) { + (receipt = await this.token.burn(amount, data, { from })); + expectEvent(receipt, 'Burned', { + operator: from, + from, + amount, + data, + operatorData: null, + }); + } else { + (receipt = await this.token.operatorBurn(from, amount, data, operatorData, { from: operator })); + expectEvent(receipt, 'Burned', { + operator, + from, + amount, + data, + operatorData, + }); + } + + expectEvent(receipt, 'Transfer', { + from, + to: ZERO_ADDRESS, + value: amount, + }); + + const finalTotalSupply = await this.token.totalSupply(); + const finalFromBalance = await this.token.balanceOf(from); + + expect(finalTotalSupply.sub(initialTotalSupply)).to.be.bignumber.equal(amount.neg()); + expect(finalFromBalance.sub(initialFromBalance)).to.be.bignumber.equal(amount.neg()); + }); +} + +function shouldBehaveLikeERC777InternalMint (recipient, operator, amount, data, operatorData) { + shouldInternalMintTokens(operator, recipient, new BN('0'), data, operatorData); + shouldInternalMintTokens(operator, recipient, amount, data, operatorData); + + it('reverts when minting tokens for the zero address', async function () { + await expectRevert.unspecified( + this.token.mintInternal(ZERO_ADDRESS, amount, data, operatorData, { from: operator }), + ); + }); +} + +function shouldInternalMintTokens (operator, to, amount, data, operatorData) { + it(`can (internal) mint an amount of ${amount}`, async function () { + const initialTotalSupply = await this.token.totalSupply(); + const initialToBalance = await this.token.balanceOf(to); + + const receipt = await this.token.mintInternal(to, amount, data, operatorData, { from: operator }); + + expectEvent(receipt, 'Minted', { + operator, + to, + amount, + data, + operatorData, + }); + + expectEvent(receipt, 'Transfer', { + from: ZERO_ADDRESS, + to, + value: amount, + }); + + const finalTotalSupply = await this.token.totalSupply(); + const finalToBalance = await this.token.balanceOf(to); + + expect(finalTotalSupply.sub(initialTotalSupply)).to.be.bignumber.equal(amount); + expect(finalToBalance.sub(initialToBalance)).to.be.bignumber.equal(amount); + }); +} + +function shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook (operator, amount, data, operatorData) { + context('when TokensRecipient reverts', function () { + beforeEach(async function () { + await this.tokensRecipientImplementer.setShouldRevertReceive(true); + }); + + it('send reverts', async function () { + await expectRevert.unspecified(sendFromHolder(this.token, this.sender, this.recipient, amount, data)); + }); + + it('operatorSend reverts', async function () { + await expectRevert.unspecified( + this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }), + ); + }); + + it('mint (internal) reverts', async function () { + await expectRevert.unspecified( + this.token.mintInternal(this.recipient, amount, data, operatorData, { from: operator }), + ); + }); + }); + + context('when TokensRecipient does not revert', function () { + beforeEach(async function () { + await this.tokensRecipientImplementer.setShouldRevertSend(false); + }); + + it('TokensRecipient receives send data and is called after state mutation', async function () { + const { tx } = await sendFromHolder(this.token, this.sender, this.recipient, amount, data); + + const postSenderBalance = await this.token.balanceOf(this.sender); + const postRecipientBalance = await this.token.balanceOf(this.recipient); + + await assertTokensReceivedCalled( + this.token, + tx, + this.sender, + this.sender, + this.recipient, + amount, + data, + null, + postSenderBalance, + postRecipientBalance, + ); + }); + + it('TokensRecipient receives operatorSend data and is called after state mutation', async function () { + const { tx } = await this.token.operatorSend( + this.sender, this.recipient, amount, data, operatorData, + { from: operator }, + ); + + const postSenderBalance = await this.token.balanceOf(this.sender); + const postRecipientBalance = await this.token.balanceOf(this.recipient); + + await assertTokensReceivedCalled( + this.token, + tx, + operator, + this.sender, + this.recipient, + amount, + data, + operatorData, + postSenderBalance, + postRecipientBalance, + ); + }); + + it('TokensRecipient receives mint (internal) data and is called after state mutation', async function () { + const { tx } = await this.token.mintInternal( + this.recipient, amount, data, operatorData, { from: operator }, + ); + + const postRecipientBalance = await this.token.balanceOf(this.recipient); + + await assertTokensReceivedCalled( + this.token, + tx, + operator, + ZERO_ADDRESS, + this.recipient, + amount, + data, + operatorData, + new BN('0'), + postRecipientBalance, + ); + }); + }); +} + +function shouldBehaveLikeERC777SendBurnWithSendHook (operator, amount, data, operatorData) { + context('when TokensSender reverts', function () { + beforeEach(async function () { + await this.tokensSenderImplementer.setShouldRevertSend(true); + }); + + it('send reverts', async function () { + await expectRevert.unspecified(sendFromHolder(this.token, this.sender, this.recipient, amount, data)); + }); + + it('operatorSend reverts', async function () { + await expectRevert.unspecified( + this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }), + ); + }); + + it('burn reverts', async function () { + await expectRevert.unspecified(burnFromHolder(this.token, this.sender, amount, data)); + }); + + it('operatorBurn reverts', async function () { + await expectRevert.unspecified( + this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }), + ); + }); + }); + + context('when TokensSender does not revert', function () { + beforeEach(async function () { + await this.tokensSenderImplementer.setShouldRevertSend(false); + }); + + it('TokensSender receives send data and is called before state mutation', async function () { + const preSenderBalance = await this.token.balanceOf(this.sender); + const preRecipientBalance = await this.token.balanceOf(this.recipient); + + const { tx } = await sendFromHolder(this.token, this.sender, this.recipient, amount, data); + + await assertTokensToSendCalled( + this.token, + tx, + this.sender, + this.sender, + this.recipient, + amount, + data, + null, + preSenderBalance, + preRecipientBalance, + ); + }); + + it('TokensSender receives operatorSend data and is called before state mutation', async function () { + const preSenderBalance = await this.token.balanceOf(this.sender); + const preRecipientBalance = await this.token.balanceOf(this.recipient); + + const { tx } = await this.token.operatorSend( + this.sender, this.recipient, amount, data, operatorData, + { from: operator }, + ); + + await assertTokensToSendCalled( + this.token, + tx, + operator, + this.sender, + this.recipient, + amount, + data, + operatorData, + preSenderBalance, + preRecipientBalance, + ); + }); + + it('TokensSender receives burn data and is called before state mutation', async function () { + const preSenderBalance = await this.token.balanceOf(this.sender); + + const { tx } = await burnFromHolder(this.token, this.sender, amount, data, { from: this.sender }); + + await assertTokensToSendCalled( + this.token, tx, this.sender, this.sender, ZERO_ADDRESS, amount, data, null, preSenderBalance, + ); + }); + + it('TokensSender receives operatorBurn data and is called before state mutation', async function () { + const preSenderBalance = await this.token.balanceOf(this.sender); + + const { tx } = await this.token.operatorBurn(this.sender, amount, data, operatorData, { from: operator }); + + await assertTokensToSendCalled( + this.token, tx, operator, this.sender, ZERO_ADDRESS, amount, data, operatorData, preSenderBalance, + ); + }); + }); +} + +function removeBalance (holder) { + beforeEach(async function () { + await this.token.burn(await this.token.balanceOf(holder), '0x', { from: holder }); + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal('0'); + }); +} + +async function assertTokensReceivedCalled (token, txHash, operator, from, to, amount, data, operatorData, fromBalance, + toBalance = '0') { + await expectEvent.inTransaction(txHash, ERC777SenderRecipientMock, 'TokensReceivedCalled', { + operator, from, to, amount, data, operatorData, token: token.address, fromBalance, toBalance, + }); +} + +async function assertTokensToSendCalled (token, txHash, operator, from, to, amount, data, operatorData, fromBalance, + toBalance = '0') { + await expectEvent.inTransaction(txHash, ERC777SenderRecipientMock, 'TokensToSendCalled', { + operator, from, to, amount, data, operatorData, token: token.address, fromBalance, toBalance, + }); +} + +async function sendFromHolder (token, holder, to, amount, data) { + if ((await web3.eth.getCode(holder)).length <= '0x'.length) { + return token.send(to, amount, data, { from: holder }); + } else { + // assume holder is ERC777SenderRecipientMock contract + return (await ERC777SenderRecipientMock.at(holder)).send(token.address, to, amount, data); + } +} + +async function burnFromHolder (token, holder, amount, data) { + if ((await web3.eth.getCode(holder)).length <= '0x'.length) { + return token.burn(amount, data, { from: holder }); + } else { + // assume holder is ERC777SenderRecipientMock contract + return (await ERC777SenderRecipientMock.at(holder)).burn(token.address, amount, data); + } +} + +module.exports = { + shouldBehaveLikeERC777DirectSendBurn, + shouldBehaveLikeERC777OperatorSendBurn, + shouldBehaveLikeERC777UnauthorizedOperatorSendBurn, + shouldBehaveLikeERC777InternalMint, + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook, + shouldBehaveLikeERC777SendBurnWithSendHook, +}; diff --git a/lib/openzeppelin-contracts/test/token/ERC777/ERC777.test.js b/lib/openzeppelin-contracts/test/token/ERC777/ERC777.test.js new file mode 100644 index 0000000..51da130 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC777/ERC777.test.js @@ -0,0 +1,610 @@ +const { BN, constants, expectEvent, expectRevert, singletons } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const { + shouldBehaveLikeERC777DirectSendBurn, + shouldBehaveLikeERC777OperatorSendBurn, + shouldBehaveLikeERC777UnauthorizedOperatorSendBurn, + shouldBehaveLikeERC777InternalMint, + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook, + shouldBehaveLikeERC777SendBurnWithSendHook, +} = require('./ERC777.behavior'); + +const { + shouldBehaveLikeERC20, + shouldBehaveLikeERC20Approve, +} = require('../ERC20/ERC20.behavior'); + +const ERC777 = artifacts.require('ERC777Mock'); +const ERC777SenderRecipientMock = artifacts.require('ERC777SenderRecipientMock'); + +contract('ERC777', function (accounts) { + const [ registryFunder, holder, defaultOperatorA, defaultOperatorB, newOperator, anyone ] = accounts; + + const initialSupply = new BN('10000'); + const name = 'ERC777Test'; + const symbol = '777T'; + const data = web3.utils.sha3('OZ777TestData'); + const operatorData = web3.utils.sha3('OZ777TestOperatorData'); + + const defaultOperators = [defaultOperatorA, defaultOperatorB]; + + beforeEach(async function () { + this.erc1820 = await singletons.ERC1820Registry(registryFunder); + }); + + context('with default operators', function () { + beforeEach(async function () { + this.token = await ERC777.new(holder, initialSupply, name, symbol, defaultOperators); + }); + + describe('as an ERC20 token', function () { + shouldBehaveLikeERC20('ERC777', initialSupply, holder, anyone, defaultOperatorA); + + describe('_approve', function () { + shouldBehaveLikeERC20Approve('ERC777', holder, anyone, initialSupply, function (owner, spender, amount) { + return this.token.approveInternal(owner, spender, amount); + }); + + describe('when the owner is the zero address', function () { + it('reverts', async function () { + await expectRevert(this.token.approveInternal(ZERO_ADDRESS, anyone, initialSupply), + 'ERC777: approve from the zero address', + ); + }); + }); + }); + }); + + it('does not emit AuthorizedOperator events for default operators', async function () { + await expectEvent.notEmitted.inConstruction(this.token, 'AuthorizedOperator'); + }); + + describe('basic information', function () { + it('returns the name', async function () { + expect(await this.token.name()).to.equal(name); + }); + + it('returns the symbol', async function () { + expect(await this.token.symbol()).to.equal(symbol); + }); + + it('returns a granularity of 1', async function () { + expect(await this.token.granularity()).to.be.bignumber.equal('1'); + }); + + it('returns the default operators', async function () { + expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); + }); + + it('default operators are operators for all accounts', async function () { + for (const operator of defaultOperators) { + expect(await this.token.isOperatorFor(operator, anyone)).to.equal(true); + } + }); + + it('returns the total supply', async function () { + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); + }); + + it('returns 18 when decimals is called', async function () { + expect(await this.token.decimals()).to.be.bignumber.equal('18'); + }); + + it('the ERC777Token interface is registered in the registry', async function () { + expect(await this.erc1820.getInterfaceImplementer(this.token.address, web3.utils.soliditySha3('ERC777Token'))) + .to.equal(this.token.address); + }); + + it('the ERC20Token interface is registered in the registry', async function () { + expect(await this.erc1820.getInterfaceImplementer(this.token.address, web3.utils.soliditySha3('ERC20Token'))) + .to.equal(this.token.address); + }); + }); + + describe('balanceOf', function () { + context('for an account with no tokens', function () { + it('returns zero', async function () { + expect(await this.token.balanceOf(anyone)).to.be.bignumber.equal('0'); + }); + }); + + context('for an account with tokens', function () { + it('returns their balance', async function () { + expect(await this.token.balanceOf(holder)).to.be.bignumber.equal(initialSupply); + }); + }); + }); + + context('with no ERC777TokensSender and no ERC777TokensRecipient implementers', function () { + describe('send/burn', function () { + shouldBehaveLikeERC777DirectSendBurn(holder, anyone, data); + + context('with self operator', function () { + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, holder, data, operatorData); + }); + + context('with first default operator', function () { + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorA, data, operatorData); + }); + + context('with second default operator', function () { + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, defaultOperatorB, data, operatorData); + }); + + context('before authorizing a new operator', function () { + shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); + }); + + context('with new authorized operator', function () { + beforeEach(async function () { + await this.token.authorizeOperator(newOperator, { from: holder }); + }); + + shouldBehaveLikeERC777OperatorSendBurn(holder, anyone, newOperator, data, operatorData); + + context('with revoked operator', function () { + beforeEach(async function () { + await this.token.revokeOperator(newOperator, { from: holder }); + }); + + shouldBehaveLikeERC777UnauthorizedOperatorSendBurn(holder, anyone, newOperator, data, operatorData); + }); + }); + }); + + describe('mint (internal)', function () { + const to = anyone; + const amount = new BN('5'); + + context('with default operator', function () { + const operator = defaultOperatorA; + + shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); + }); + + context('with non operator', function () { + const operator = newOperator; + + shouldBehaveLikeERC777InternalMint(to, operator, amount, data, operatorData); + }); + }); + + describe('mint (internal extended)', function () { + const amount = new BN('5'); + + context('to anyone', function () { + beforeEach(async function () { + this.recipient = anyone; + }); + + context('with default operator', function () { + const operator = defaultOperatorA; + + it('without requireReceptionAck', async function () { + await this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + false, + { from: operator }, + ); + }); + + it('with requireReceptionAck', async function () { + await this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + true, + { from: operator }, + ); + }); + }); + + context('with non operator', function () { + const operator = newOperator; + + it('without requireReceptionAck', async function () { + await this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + false, + { from: operator }, + ); + }); + + it('with requireReceptionAck', async function () { + await this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + true, + { from: operator }, + ); + }); + }); + }); + + context('to non ERC777TokensRecipient implementer', function () { + beforeEach(async function () { + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = this.tokensRecipientImplementer.address; + }); + + context('with default operator', function () { + const operator = defaultOperatorA; + + it('without requireReceptionAck', async function () { + await this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + false, + { from: operator }, + ); + }); + + it('with requireReceptionAck', async function () { + await expectRevert( + this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + true, + { from: operator }, + ), + 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', + ); + }); + }); + + context('with non operator', function () { + const operator = newOperator; + + it('without requireReceptionAck', async function () { + await this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + false, + { from: operator }, + ); + }); + + it('with requireReceptionAck', async function () { + await expectRevert( + this.token.mintInternalExtended( + this.recipient, + amount, + data, + operatorData, + true, + { from: operator }, + ), + 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', + ); + }); + }); + }); + }); + }); + + describe('operator management', function () { + it('accounts are their own operator', async function () { + expect(await this.token.isOperatorFor(holder, holder)).to.equal(true); + }); + + it('reverts when self-authorizing', async function () { + await expectRevert( + this.token.authorizeOperator(holder, { from: holder }), 'ERC777: authorizing self as operator', + ); + }); + + it('reverts when self-revoking', async function () { + await expectRevert( + this.token.revokeOperator(holder, { from: holder }), 'ERC777: revoking self as operator', + ); + }); + + it('non-operators can be revoked', async function () { + expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); + + const receipt = await this.token.revokeOperator(newOperator, { from: holder }); + expectEvent(receipt, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); + }); + + it('non-operators can be authorized', async function () { + expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); + + const receipt = await this.token.authorizeOperator(newOperator, { from: holder }); + expectEvent(receipt, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(true); + }); + + describe('new operators', function () { + beforeEach(async function () { + await this.token.authorizeOperator(newOperator, { from: holder }); + }); + + it('are not added to the default operators list', async function () { + expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); + }); + + it('can be re-authorized', async function () { + const receipt = await this.token.authorizeOperator(newOperator, { from: holder }); + expectEvent(receipt, 'AuthorizedOperator', { operator: newOperator, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(true); + }); + + it('can be revoked', async function () { + const receipt = await this.token.revokeOperator(newOperator, { from: holder }); + expectEvent(receipt, 'RevokedOperator', { operator: newOperator, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(newOperator, holder)).to.equal(false); + }); + }); + + describe('default operators', function () { + it('can be re-authorized', async function () { + const receipt = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); + expectEvent(receipt, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(defaultOperatorA, holder)).to.equal(true); + }); + + it('can be revoked', async function () { + const receipt = await this.token.revokeOperator(defaultOperatorA, { from: holder }); + expectEvent(receipt, 'RevokedOperator', { operator: defaultOperatorA, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(defaultOperatorA, holder)).to.equal(false); + }); + + it('cannot be revoked for themselves', async function () { + await expectRevert( + this.token.revokeOperator(defaultOperatorA, { from: defaultOperatorA }), + 'ERC777: revoking self as operator', + ); + }); + + context('with revoked default operator', function () { + beforeEach(async function () { + await this.token.revokeOperator(defaultOperatorA, { from: holder }); + }); + + it('default operator is not revoked for other holders', async function () { + expect(await this.token.isOperatorFor(defaultOperatorA, anyone)).to.equal(true); + }); + + it('other default operators are not revoked', async function () { + expect(await this.token.isOperatorFor(defaultOperatorB, holder)).to.equal(true); + }); + + it('default operators list is not modified', async function () { + expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); + }); + + it('revoked default operator can be re-authorized', async function () { + const receipt = await this.token.authorizeOperator(defaultOperatorA, { from: holder }); + expectEvent(receipt, 'AuthorizedOperator', { operator: defaultOperatorA, tokenHolder: holder }); + + expect(await this.token.isOperatorFor(defaultOperatorA, holder)).to.equal(true); + }); + }); + }); + }); + + describe('send and receive hooks', function () { + const amount = new BN('1'); + const operator = defaultOperatorA; + // sender and recipient are stored inside 'this', since in some tests their addresses are determined dynamically + + describe('tokensReceived', function () { + beforeEach(function () { + this.sender = holder; + }); + + context('with no ERC777TokensRecipient implementer', function () { + context('with contract recipient', function () { + beforeEach(async function () { + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = this.tokensRecipientImplementer.address; + + // Note that tokensRecipientImplementer doesn't implement the recipient interface for the recipient + }); + + it('send reverts', async function () { + await expectRevert( + this.token.send(this.recipient, amount, data, { from: holder }), + 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', + ); + }); + + it('operatorSend reverts', async function () { + await expectRevert( + this.token.operatorSend(this.sender, this.recipient, amount, data, operatorData, { from: operator }), + 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', + ); + }); + + it('mint (internal) reverts', async function () { + await expectRevert( + this.token.mintInternal(this.recipient, amount, data, operatorData, { from: operator }), + 'ERC777: token recipient contract has no implementer for ERC777TokensRecipient', + ); + }); + + it('(ERC20) transfer succeeds', async function () { + await this.token.transfer(this.recipient, amount, { from: holder }); + }); + + it('(ERC20) transferFrom succeeds', async function () { + const approved = anyone; + await this.token.approve(approved, amount, { from: this.sender }); + await this.token.transferFrom(this.sender, this.recipient, amount, { from: approved }); + }); + }); + }); + + context('with ERC777TokensRecipient implementer', function () { + context('with contract as implementer for an externally owned account', function () { + beforeEach(async function () { + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = anyone; + + await this.tokensRecipientImplementer.recipientFor(this.recipient); + + await this.erc1820.setInterfaceImplementer( + this.recipient, + web3.utils.soliditySha3('ERC777TokensRecipient'), this.tokensRecipientImplementer.address, + { from: this.recipient }, + ); + }); + + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); + }); + + context('with contract as implementer for another contract', function () { + beforeEach(async function () { + this.recipientContract = await ERC777SenderRecipientMock.new(); + this.recipient = this.recipientContract.address; + + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + await this.tokensRecipientImplementer.recipientFor(this.recipient); + await this.recipientContract.registerRecipient(this.tokensRecipientImplementer.address); + }); + + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); + }); + + context('with contract as implementer for itself', function () { + beforeEach(async function () { + this.tokensRecipientImplementer = await ERC777SenderRecipientMock.new(); + this.recipient = this.tokensRecipientImplementer.address; + + await this.tokensRecipientImplementer.recipientFor(this.recipient); + }); + + shouldBehaveLikeERC777SendBurnMintInternalWithReceiveHook(operator, amount, data, operatorData); + }); + }); + }); + + describe('tokensToSend', function () { + beforeEach(function () { + this.recipient = anyone; + }); + + context('with a contract as implementer for an externally owned account', function () { + beforeEach(async function () { + this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); + this.sender = holder; + + await this.tokensSenderImplementer.senderFor(this.sender); + + await this.erc1820.setInterfaceImplementer( + this.sender, + web3.utils.soliditySha3('ERC777TokensSender'), this.tokensSenderImplementer.address, + { from: this.sender }, + ); + }); + + shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + }); + + context('with contract as implementer for another contract', function () { + beforeEach(async function () { + this.senderContract = await ERC777SenderRecipientMock.new(); + this.sender = this.senderContract.address; + + this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); + await this.tokensSenderImplementer.senderFor(this.sender); + await this.senderContract.registerSender(this.tokensSenderImplementer.address); + + // For the contract to be able to receive tokens (that it can later send), it must also implement the + // recipient interface. + + await this.senderContract.recipientFor(this.sender); + await this.token.send(this.sender, amount, data, { from: holder }); + }); + + shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + }); + + context('with a contract as implementer for itself', function () { + beforeEach(async function () { + this.tokensSenderImplementer = await ERC777SenderRecipientMock.new(); + this.sender = this.tokensSenderImplementer.address; + + await this.tokensSenderImplementer.senderFor(this.sender); + + // For the contract to be able to receive tokens (that it can later send), it must also implement the + // recipient interface. + + await this.tokensSenderImplementer.recipientFor(this.sender); + await this.token.send(this.sender, amount, data, { from: holder }); + }); + + shouldBehaveLikeERC777SendBurnWithSendHook(operator, amount, data, operatorData); + }); + }); + }); + }); + + context('with no default operators', function () { + beforeEach(async function () { + this.token = await ERC777.new(holder, initialSupply, name, symbol, []); + }); + + it('default operators list is empty', async function () { + expect(await this.token.defaultOperators()).to.deep.equal([]); + }); + }); + + describe('relative order of hooks', function () { + beforeEach(async function () { + await singletons.ERC1820Registry(registryFunder); + this.sender = await ERC777SenderRecipientMock.new(); + await this.sender.registerRecipient(this.sender.address); + await this.sender.registerSender(this.sender.address); + this.token = await ERC777.new(holder, initialSupply, name, symbol, []); + await this.token.send(this.sender.address, 1, '0x', { from: holder }); + }); + + it('send', async function () { + const { receipt } = await this.sender.send(this.token.address, anyone, 1, '0x'); + + const internalBeforeHook = receipt.logs.findIndex(l => l.event === 'BeforeTokenTransfer'); + expect(internalBeforeHook).to.be.gte(0); + const externalSendHook = receipt.logs.findIndex(l => l.event === 'TokensToSendCalled'); + expect(externalSendHook).to.be.gte(0); + + expect(externalSendHook).to.be.lt(internalBeforeHook); + }); + + it('burn', async function () { + const { receipt } = await this.sender.burn(this.token.address, 1, '0x'); + + const internalBeforeHook = receipt.logs.findIndex(l => l.event === 'BeforeTokenTransfer'); + expect(internalBeforeHook).to.be.gte(0); + const externalSendHook = receipt.logs.findIndex(l => l.event === 'TokensToSendCalled'); + expect(externalSendHook).to.be.gte(0); + + expect(externalSendHook).to.be.lt(internalBeforeHook); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/ERC777/presets/ERC777PresetFixedSupply.test.js b/lib/openzeppelin-contracts/test/token/ERC777/presets/ERC777PresetFixedSupply.test.js new file mode 100644 index 0000000..e6a842b --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/ERC777/presets/ERC777PresetFixedSupply.test.js @@ -0,0 +1,49 @@ +const { BN, singletons } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC777PresetFixedSupply = artifacts.require('ERC777PresetFixedSupply'); + +contract('ERC777PresetFixedSupply', function (accounts) { + const [registryFunder, owner, defaultOperatorA, defaultOperatorB, anyone] = accounts; + + const initialSupply = new BN('10000'); + const name = 'ERC777Preset'; + const symbol = '777P'; + + const defaultOperators = [defaultOperatorA, defaultOperatorB]; + + before(async function () { + await singletons.ERC1820Registry(registryFunder); + }); + + beforeEach(async function () { + this.token = await ERC777PresetFixedSupply.new(name, symbol, defaultOperators, initialSupply, owner); + }); + + it('returns the name', async function () { + expect(await this.token.name()).to.equal(name); + }); + + it('returns the symbol', async function () { + expect(await this.token.symbol()).to.equal(symbol); + }); + + it('returns the default operators', async function () { + expect(await this.token.defaultOperators()).to.deep.equal(defaultOperators); + }); + + it('default operators are operators for all accounts', async function () { + for (const operator of defaultOperators) { + expect(await this.token.isOperatorFor(operator, anyone)).to.equal(true); + } + }); + + it('returns the total supply equal to initial supply', async function () { + expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply); + }); + + it('returns the balance of owner equal to initial supply', async function () { + expect(await this.token.balanceOf(owner)).to.be.bignumber.equal(initialSupply); + }); +}); diff --git a/lib/openzeppelin-contracts/test/token/common/ERC2981.behavior.js b/lib/openzeppelin-contracts/test/token/common/ERC2981.behavior.js new file mode 100644 index 0000000..2fd2747 --- /dev/null +++ b/lib/openzeppelin-contracts/test/token/common/ERC2981.behavior.js @@ -0,0 +1,160 @@ +const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { ZERO_ADDRESS } = constants; + +const { shouldSupportInterfaces } = require('../../utils/introspection/SupportsInterface.behavior'); + +function shouldBehaveLikeERC2981 () { + const royaltyFraction = new BN('10'); + + shouldSupportInterfaces(['ERC2981']); + + describe('default royalty', function () { + beforeEach(async function () { + await this.token.setDefaultRoyalty(this.account1, royaltyFraction); + }); + + it('checks royalty is set', async function () { + const royalty = new BN((this.salePrice * royaltyFraction) / 10000); + + const initInfo = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + + expect(initInfo[0]).to.be.equal(this.account1); + expect(initInfo[1]).to.be.bignumber.equal(royalty); + }); + + it('updates royalty amount', async function () { + const newPercentage = new BN('25'); + + // Updated royalty check + await this.token.setDefaultRoyalty(this.account1, newPercentage); + const royalty = new BN((this.salePrice * newPercentage) / 10000); + const newInfo = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + + expect(newInfo[0]).to.be.equal(this.account1); + expect(newInfo[1]).to.be.bignumber.equal(royalty); + }); + + it('holds same royalty value for different tokens', async function () { + const newPercentage = new BN('20'); + await this.token.setDefaultRoyalty(this.account1, newPercentage); + + const token1Info = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + const token2Info = await this.token.royaltyInfo(this.tokenId2, this.salePrice); + + expect(token1Info[1]).to.be.bignumber.equal(token2Info[1]); + }); + + it('Remove royalty information', async function () { + const newValue = new BN('0'); + await this.token.deleteDefaultRoyalty(); + + const token1Info = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + const token2Info = await this.token.royaltyInfo(this.tokenId2, this.salePrice); + // Test royalty info is still persistent across all tokens + expect(token1Info[0]).to.be.bignumber.equal(token2Info[0]); + expect(token1Info[1]).to.be.bignumber.equal(token2Info[1]); + // Test information was deleted + expect(token1Info[0]).to.be.equal(ZERO_ADDRESS); + expect(token1Info[1]).to.be.bignumber.equal(newValue); + }); + + it('reverts if invalid parameters', async function () { + await expectRevert( + this.token.setDefaultRoyalty(ZERO_ADDRESS, royaltyFraction), + 'ERC2981: invalid receiver', + ); + + await expectRevert( + this.token.setDefaultRoyalty(this.account1, new BN('11000')), + 'ERC2981: royalty fee will exceed salePrice', + ); + }); + }); + + describe('token based royalty', function () { + beforeEach(async function () { + await this.token.setTokenRoyalty(this.tokenId1, this.account1, royaltyFraction); + }); + + it('updates royalty amount', async function () { + const newPercentage = new BN('25'); + let royalty = new BN((this.salePrice * royaltyFraction) / 10000); + // Initial royalty check + const initInfo = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + + expect(initInfo[0]).to.be.equal(this.account1); + expect(initInfo[1]).to.be.bignumber.equal(royalty); + + // Updated royalty check + await this.token.setTokenRoyalty(this.tokenId1, this.account1, newPercentage); + royalty = new BN((this.salePrice * newPercentage) / 10000); + const newInfo = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + + expect(newInfo[0]).to.be.equal(this.account1); + expect(newInfo[1]).to.be.bignumber.equal(royalty); + }); + + it('holds different values for different tokens', async function () { + const newPercentage = new BN('20'); + await this.token.setTokenRoyalty(this.tokenId2, this.account1, newPercentage); + + const token1Info = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + const token2Info = await this.token.royaltyInfo(this.tokenId2, this.salePrice); + + // must be different even at the same this.salePrice + expect(token1Info[1]).to.not.be.equal(token2Info.royaltyFraction); + }); + + it('reverts if invalid parameters', async function () { + await expectRevert( + this.token.setTokenRoyalty(this.tokenId1, ZERO_ADDRESS, royaltyFraction), + 'ERC2981: Invalid parameters', + ); + + await expectRevert( + this.token.setTokenRoyalty(this.tokenId1, this.account1, new BN('11000')), + 'ERC2981: royalty fee will exceed salePrice', + ); + }); + + it('can reset token after setting royalty', async function () { + const newPercentage = new BN('30'); + const royalty = new BN((this.salePrice * newPercentage) / 10000); + await this.token.setTokenRoyalty(this.tokenId1, this.account2, newPercentage); + + const tokenInfo = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + + // Tokens must have own information + expect(tokenInfo[1]).to.be.bignumber.equal(royalty); + expect(tokenInfo[0]).to.be.equal(this.account2); + + await this.token.setTokenRoyalty(this.tokenId2, this.account1, new BN('0')); + const result = await this.token.royaltyInfo(this.tokenId2, this.salePrice); + // Token must not share default information + expect(result[0]).to.be.equal(this.account1); + expect(result[1]).to.be.bignumber.equal(new BN('0')); + }); + + it('can hold default and token royalty information', async function () { + const newPercentage = new BN('30'); + const royalty = new BN((this.salePrice * newPercentage) / 10000); + + await this.token.setTokenRoyalty(this.tokenId2, this.account2, newPercentage); + + const token1Info = await this.token.royaltyInfo(this.tokenId1, this.salePrice); + const token2Info = await this.token.royaltyInfo(this.tokenId2, this.salePrice); + // Tokens must not have same values + expect(token1Info[1]).to.not.be.bignumber.equal(token2Info[1]); + expect(token1Info[0]).to.not.be.equal(token2Info[0]); + + // Updated token must have new values + expect(token2Info[0]).to.be.equal(this.account2); + expect(token2Info[1]).to.be.bignumber.equal(royalty); + }); + }); +} + +module.exports = { + shouldBehaveLikeERC2981, +}; diff --git a/lib/openzeppelin-contracts/test/utils/Address.test.js b/lib/openzeppelin-contracts/test/utils/Address.test.js new file mode 100644 index 0000000..1bdfad4 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Address.test.js @@ -0,0 +1,382 @@ +const { balance, ether, expectRevert, send, expectEvent } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const AddressImpl = artifacts.require('AddressImpl'); +const EtherReceiver = artifacts.require('EtherReceiverMock'); +const CallReceiverMock = artifacts.require('CallReceiverMock'); + +contract('Address', function (accounts) { + const [ recipient, other ] = accounts; + + beforeEach(async function () { + this.mock = await AddressImpl.new(); + }); + + describe('isContract', function () { + it('returns false for account address', async function () { + expect(await this.mock.isContract(other)).to.equal(false); + }); + + it('returns true for contract address', async function () { + const contract = await AddressImpl.new(); + expect(await this.mock.isContract(contract.address)).to.equal(true); + }); + }); + + describe('sendValue', function () { + beforeEach(async function () { + this.recipientTracker = await balance.tracker(recipient); + }); + + context('when sender contract has no funds', function () { + it('sends 0 wei', async function () { + await this.mock.sendValue(other, 0); + + expect(await this.recipientTracker.delta()).to.be.bignumber.equal('0'); + }); + + it('reverts when sending non-zero amounts', async function () { + await expectRevert(this.mock.sendValue(other, 1), 'Address: insufficient balance'); + }); + }); + + context('when sender contract has funds', function () { + const funds = ether('1'); + beforeEach(async function () { + await send.ether(other, this.mock.address, funds); + }); + + it('sends 0 wei', async function () { + await this.mock.sendValue(recipient, 0); + expect(await this.recipientTracker.delta()).to.be.bignumber.equal('0'); + }); + + it('sends non-zero amounts', async function () { + await this.mock.sendValue(recipient, funds.subn(1)); + expect(await this.recipientTracker.delta()).to.be.bignumber.equal(funds.subn(1)); + }); + + it('sends the whole balance', async function () { + await this.mock.sendValue(recipient, funds); + expect(await this.recipientTracker.delta()).to.be.bignumber.equal(funds); + expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0'); + }); + + it('reverts when sending more than the balance', async function () { + await expectRevert(this.mock.sendValue(recipient, funds.addn(1)), 'Address: insufficient balance'); + }); + + context('with contract recipient', function () { + beforeEach(async function () { + this.contractRecipient = await EtherReceiver.new(); + }); + + it('sends funds', async function () { + const tracker = await balance.tracker(this.contractRecipient.address); + + await this.contractRecipient.setAcceptEther(true); + await this.mock.sendValue(this.contractRecipient.address, funds); + expect(await tracker.delta()).to.be.bignumber.equal(funds); + }); + + it('reverts on recipient revert', async function () { + await this.contractRecipient.setAcceptEther(false); + await expectRevert( + this.mock.sendValue(this.contractRecipient.address, funds), + 'Address: unable to send value, recipient may have reverted', + ); + }); + }); + }); + }); + + describe('functionCall', function () { + beforeEach(async function () { + this.contractRecipient = await CallReceiverMock.new(); + }); + + context('with valid contract receiver', function () { + it('calls the requested function', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + + const receipt = await this.mock.functionCall(this.contractRecipient.address, abiEncodedCall); + + expectEvent(receipt, 'CallReturnValue', { data: '0x1234' }); + await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled'); + }); + + it('reverts when the called function reverts with no reason', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionRevertsNoReason', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), + 'Address: low-level call failed', + ); + }); + + it('reverts when the called function reverts, bubbling up the revert reason', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionRevertsReason', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), + 'CallReceiverMock: reverting', + ); + }); + + it('reverts when the called function runs out of gas', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionOutOfGas', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionCall(this.contractRecipient.address, abiEncodedCall, { gas: '120000' }), + 'Address: low-level call failed', + ); + }); + + it('reverts when the called function throws', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionThrows', + type: 'function', + inputs: [], + }, []); + + await expectRevert.unspecified( + this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), + ); + }); + + it('reverts when function does not exist', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionDoesNotExist', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionCall(this.contractRecipient.address, abiEncodedCall), + 'Address: low-level call failed', + ); + }); + }); + + context('with non-contract receiver', function () { + it('reverts when address is not a contract', async function () { + const [ recipient ] = accounts; + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + await expectRevert(this.mock.functionCall(recipient, abiEncodedCall), 'Address: call to non-contract'); + }); + }); + }); + + describe('functionCallWithValue', function () { + beforeEach(async function () { + this.contractRecipient = await CallReceiverMock.new(); + }); + + context('with zero value', function () { + it('calls the requested function', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + + const receipt = await this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, 0); + + expectEvent(receipt, 'CallReturnValue', { data: '0x1234' }); + await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled'); + }); + }); + + context('with non-zero value', function () { + const amount = ether('1.2'); + + it('reverts if insufficient sender balance', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount), + 'Address: insufficient balance for call', + ); + }); + + it('calls the requested function with existing value', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + + const tracker = await balance.tracker(this.contractRecipient.address); + + await send.ether(other, this.mock.address, amount); + const receipt = await this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount); + + expect(await tracker.delta()).to.be.bignumber.equal(amount); + + expectEvent(receipt, 'CallReturnValue', { data: '0x1234' }); + await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled'); + }); + + it('calls the requested function with transaction funds', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + + const tracker = await balance.tracker(this.contractRecipient.address); + + expect(await balance.current(this.mock.address)).to.be.bignumber.equal('0'); + const receipt = await this.mock.functionCallWithValue( + this.contractRecipient.address, abiEncodedCall, amount, { from: other, value: amount }, + ); + + expect(await tracker.delta()).to.be.bignumber.equal(amount); + + expectEvent(receipt, 'CallReturnValue', { data: '0x1234' }); + await expectEvent.inTransaction(receipt.tx, CallReceiverMock, 'MockFunctionCalled'); + }); + + it('reverts when calling non-payable functions', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionNonPayable', + type: 'function', + inputs: [], + }, []); + + await send.ether(other, this.mock.address, amount); + await expectRevert( + this.mock.functionCallWithValue(this.contractRecipient.address, abiEncodedCall, amount), + 'Address: low-level call with value failed', + ); + }); + }); + }); + + describe('functionStaticCall', function () { + beforeEach(async function () { + this.contractRecipient = await CallReceiverMock.new(); + }); + + it('calls the requested function', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockStaticFunction', + type: 'function', + inputs: [], + }, []); + + const receipt = await this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall); + + expectEvent(receipt, 'CallReturnValue', { data: '0x1234' }); + }); + + it('reverts on a non-static function', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall), + 'Address: low-level static call failed', + ); + }); + + it('bubbles up revert reason', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionRevertsReason', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionStaticCall(this.contractRecipient.address, abiEncodedCall), + 'CallReceiverMock: reverting', + ); + }); + + it('reverts when address is not a contract', async function () { + const [ recipient ] = accounts; + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + await expectRevert( + this.mock.functionStaticCall(recipient, abiEncodedCall), + 'Address: call to non-contract', + ); + }); + }); + + describe('functionDelegateCall', function () { + beforeEach(async function () { + this.contractRecipient = await CallReceiverMock.new(); + }); + + it('delegate calls the requested function', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionWritesStorage', + type: 'function', + inputs: [], + }, []); + + const receipt = await this.mock.functionDelegateCall(this.contractRecipient.address, abiEncodedCall); + + expectEvent(receipt, 'CallReturnValue', { data: '0x1234' }); + + expect(await this.mock.sharedAnswer()).to.equal('42'); + }); + + it('bubbles up revert reason', async function () { + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunctionRevertsReason', + type: 'function', + inputs: [], + }, []); + + await expectRevert( + this.mock.functionDelegateCall(this.contractRecipient.address, abiEncodedCall), + 'CallReceiverMock: reverting', + ); + }); + + it('reverts when address is not a contract', async function () { + const [ recipient ] = accounts; + const abiEncodedCall = web3.eth.abi.encodeFunctionCall({ + name: 'mockFunction', + type: 'function', + inputs: [], + }, []); + await expectRevert( + this.mock.functionDelegateCall(recipient, abiEncodedCall), + 'Address: call to non-contract', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Arrays.test.js b/lib/openzeppelin-contracts/test/utils/Arrays.test.js new file mode 100644 index 0000000..8c287cc --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Arrays.test.js @@ -0,0 +1,105 @@ +require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const AddressArraysMock = artifacts.require('AddressArraysMock'); +const Bytes32ArraysMock = artifacts.require('Bytes32ArraysMock'); +const Uint256ArraysMock = artifacts.require('Uint256ArraysMock'); + +contract('Arrays', function (accounts) { + describe('findUpperBound', function () { + context('Even number of elements', function () { + const EVEN_ELEMENTS_ARRAY = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + + beforeEach(async function () { + this.arrays = await Uint256ArraysMock.new(EVEN_ELEMENTS_ARRAY); + }); + + it('returns correct index for the basic case', async function () { + expect(await this.arrays.findUpperBound(16)).to.be.bignumber.equal('5'); + }); + + it('returns 0 for the first element', async function () { + expect(await this.arrays.findUpperBound(11)).to.be.bignumber.equal('0'); + }); + + it('returns index of the last element', async function () { + expect(await this.arrays.findUpperBound(20)).to.be.bignumber.equal('9'); + }); + + it('returns first index after last element if searched value is over the upper boundary', async function () { + expect(await this.arrays.findUpperBound(32)).to.be.bignumber.equal('10'); + }); + + it('returns 0 for the element under the lower boundary', async function () { + expect(await this.arrays.findUpperBound(2)).to.be.bignumber.equal('0'); + }); + }); + + context('Odd number of elements', function () { + const ODD_ELEMENTS_ARRAY = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]; + + beforeEach(async function () { + this.arrays = await Uint256ArraysMock.new(ODD_ELEMENTS_ARRAY); + }); + + it('returns correct index for the basic case', async function () { + expect(await this.arrays.findUpperBound(16)).to.be.bignumber.equal('5'); + }); + + it('returns 0 for the first element', async function () { + expect(await this.arrays.findUpperBound(11)).to.be.bignumber.equal('0'); + }); + + it('returns index of the last element', async function () { + expect(await this.arrays.findUpperBound(21)).to.be.bignumber.equal('10'); + }); + + it('returns first index after last element if searched value is over the upper boundary', async function () { + expect(await this.arrays.findUpperBound(32)).to.be.bignumber.equal('11'); + }); + + it('returns 0 for the element under the lower boundary', async function () { + expect(await this.arrays.findUpperBound(2)).to.be.bignumber.equal('0'); + }); + }); + + context('Array with gap', function () { + const WITH_GAP_ARRAY = [11, 12, 13, 14, 15, 20, 21, 22, 23, 24]; + + beforeEach(async function () { + this.arrays = await Uint256ArraysMock.new(WITH_GAP_ARRAY); + }); + + it('returns index of first element in next filled range', async function () { + expect(await this.arrays.findUpperBound(17)).to.be.bignumber.equal('5'); + }); + }); + + context('Empty array', function () { + beforeEach(async function () { + this.arrays = await Uint256ArraysMock.new([]); + }); + + it('always returns 0 for empty array', async function () { + expect(await this.arrays.findUpperBound(10)).to.be.bignumber.equal('0'); + }); + }); + }); + + describe('unsafeAccess', function () { + for (const { type, artifact, elements } of [ + { type: 'address', artifact: AddressArraysMock, elements: Array(10).fill().map(() => web3.utils.randomHex(20)) }, + { type: 'bytes32', artifact: Bytes32ArraysMock, elements: Array(10).fill().map(() => web3.utils.randomHex(32)) }, + { type: 'uint256', artifact: Uint256ArraysMock, elements: Array(10).fill().map(() => web3.utils.randomHex(32)) }, + ]) { + it(type, async function () { + const contract = await artifact.new(elements); + + for (const i in elements) { + expect(await contract.unsafeAccess(i)).to.be.bignumber.equal(elements[i]); + } + }); + } + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Base64.test.js b/lib/openzeppelin-contracts/test/utils/Base64.test.js new file mode 100644 index 0000000..b6ee657 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Base64.test.js @@ -0,0 +1,33 @@ +const { expect } = require('chai'); + +const Base64Mock = artifacts.require('Base64Mock'); + +contract('Strings', function () { + beforeEach(async function () { + this.base64 = await Base64Mock.new(); + }); + + describe('from bytes - base64', function () { + it('converts to base64 encoded string with double padding', async function () { + const TEST_MESSAGE = 'test'; + const input = web3.utils.asciiToHex(TEST_MESSAGE); + expect(await this.base64.encode(input)).to.equal('dGVzdA=='); + }); + + it('converts to base64 encoded string with single padding', async function () { + const TEST_MESSAGE = 'test1'; + const input = web3.utils.asciiToHex(TEST_MESSAGE); + expect(await this.base64.encode(input)).to.equal('dGVzdDE='); + }); + + it('converts to base64 encoded string without padding', async function () { + const TEST_MESSAGE = 'test12'; + const input = web3.utils.asciiToHex(TEST_MESSAGE); + expect(await this.base64.encode(input)).to.equal('dGVzdDEy'); + }); + + it('empty bytes', async function () { + expect(await this.base64.encode([])).to.equal(''); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Checkpoints.test.js b/lib/openzeppelin-contracts/test/utils/Checkpoints.test.js new file mode 100644 index 0000000..87335da --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Checkpoints.test.js @@ -0,0 +1,189 @@ +const { expectRevert, time } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const { batchInBlock } = require('../helpers/txpool'); + +const CheckpointsMock = artifacts.require('CheckpointsMock'); + +const first = (array) => array.length ? array[0] : undefined; +const last = (array) => array.length ? array[array.length - 1] : undefined; + +contract('Checkpoints', function (accounts) { + describe('History checkpoints', function () { + beforeEach(async function () { + this.checkpoint = await CheckpointsMock.new(); + }); + + describe('without checkpoints', function () { + it('returns zero as latest value', async function () { + expect(await this.checkpoint.latest()).to.be.bignumber.equal('0'); + + const ckpt = await this.checkpoint.latestCheckpoint(); + expect(ckpt[0]).to.be.equal(false); + expect(ckpt[1]).to.be.bignumber.equal('0'); + expect(ckpt[2]).to.be.bignumber.equal('0'); + }); + + it('returns zero as past value', async function () { + await time.advanceBlock(); + expect(await this.checkpoint.getAtBlock(await web3.eth.getBlockNumber() - 1)) + .to.be.bignumber.equal('0'); + expect(await this.checkpoint.getAtProbablyRecentBlock(await web3.eth.getBlockNumber() - 1)) + .to.be.bignumber.equal('0'); + }); + }); + + describe('with checkpoints', function () { + beforeEach('pushing checkpoints', async function () { + this.tx1 = await this.checkpoint.push(1); + this.tx2 = await this.checkpoint.push(2); + await time.advanceBlock(); + this.tx3 = await this.checkpoint.push(3); + await time.advanceBlock(); + await time.advanceBlock(); + }); + + it('returns latest value', async function () { + expect(await this.checkpoint.latest()).to.be.bignumber.equal('3'); + + const ckpt = await this.checkpoint.latestCheckpoint(); + expect(ckpt[0]).to.be.equal(true); + expect(ckpt[1]).to.be.bignumber.equal(web3.utils.toBN(this.tx3.receipt.blockNumber)); + expect(ckpt[2]).to.be.bignumber.equal(web3.utils.toBN('3')); + }); + + for (const fn of [ 'getAtBlock(uint256)', 'getAtProbablyRecentBlock(uint256)' ]) { + describe(`lookup: ${fn}`, function () { + it('returns past values', async function () { + expect(await this.checkpoint.methods[fn](this.tx1.receipt.blockNumber - 1)).to.be.bignumber.equal('0'); + expect(await this.checkpoint.methods[fn](this.tx1.receipt.blockNumber)).to.be.bignumber.equal('1'); + expect(await this.checkpoint.methods[fn](this.tx2.receipt.blockNumber)).to.be.bignumber.equal('2'); + // Block with no new checkpoints + expect(await this.checkpoint.methods[fn](this.tx2.receipt.blockNumber + 1)).to.be.bignumber.equal('2'); + expect(await this.checkpoint.methods[fn](this.tx3.receipt.blockNumber)).to.be.bignumber.equal('3'); + expect(await this.checkpoint.methods[fn](this.tx3.receipt.blockNumber + 1)).to.be.bignumber.equal('3'); + }); + it('reverts if block number >= current block', async function () { + await expectRevert( + this.checkpoint.methods[fn](await web3.eth.getBlockNumber()), + 'Checkpoints: block not yet mined', + ); + + await expectRevert( + this.checkpoint.methods[fn](await web3.eth.getBlockNumber() + 1), + 'Checkpoints: block not yet mined', + ); + }); + }); + } + + it('multiple checkpoints in the same block', async function () { + const lengthBefore = await this.checkpoint.length(); + + await batchInBlock([ + () => this.checkpoint.push(8, { gas: 100000 }), + () => this.checkpoint.push(9, { gas: 100000 }), + () => this.checkpoint.push(10, { gas: 100000 }), + ]); + + expect(await this.checkpoint.length()).to.be.bignumber.equal(lengthBefore.addn(1)); + expect(await this.checkpoint.latest()).to.be.bignumber.equal('10'); + }); + + it('more than 5 checkpoints', async function () { + for (let i = 4; i <= 6; i++) { + await this.checkpoint.push(i); + } + expect(await this.checkpoint.length()).to.be.bignumber.equal('6'); + const block = await web3.eth.getBlockNumber(); + // recent + expect(await this.checkpoint.getAtProbablyRecentBlock(block - 1)).to.be.bignumber.equal('5'); + // non-recent + expect(await this.checkpoint.getAtProbablyRecentBlock(block - 9)).to.be.bignumber.equal('0'); + }); + }); + }); + + for (const length of [160, 224]) { + describe(`Trace${length}`, function () { + beforeEach(async function () { + this.contract = await artifacts.require(`Checkpoints${length}Mock`).new(); + }); + + describe('without checkpoints', function () { + it('returns zero as latest value', async function () { + expect(await this.contract.latest()).to.be.bignumber.equal('0'); + + const ckpt = await this.contract.latestCheckpoint(); + expect(ckpt[0]).to.be.equal(false); + expect(ckpt[1]).to.be.bignumber.equal('0'); + expect(ckpt[2]).to.be.bignumber.equal('0'); + }); + + it('lookup returns 0', async function () { + expect(await this.contract.lowerLookup(0)).to.be.bignumber.equal('0'); + expect(await this.contract.upperLookup(0)).to.be.bignumber.equal('0'); + }); + }); + + describe('with checkpoints', function () { + beforeEach('pushing checkpoints', async function () { + this.checkpoints = [ + { key: '2', value: '17' }, + { key: '3', value: '42' }, + { key: '5', value: '101' }, + { key: '7', value: '23' }, + { key: '11', value: '99' }, + ]; + for (const { key, value } of this.checkpoints) { + await this.contract.push(key, value); + } + }); + + it('returns latest value', async function () { + expect(await this.contract.latest()).to.be.bignumber.equal(last(this.checkpoints).value); + + const ckpt = await this.contract.latestCheckpoint(); + expect(ckpt[0]).to.be.equal(true); + expect(ckpt[1]).to.be.bignumber.equal(last(this.checkpoints).key); + expect(ckpt[2]).to.be.bignumber.equal(last(this.checkpoints).value); + }); + + it('cannot push values in the past', async function () { + await expectRevert(this.contract.push(last(this.checkpoints).key - 1, '0'), 'Checkpoint: decreasing keys'); + }); + + it('can update last value', async function () { + const newValue = '42'; + + // check length before the update + expect(await this.contract.length()).to.be.bignumber.equal(this.checkpoints.length.toString()); + + // update last key + await this.contract.push(last(this.checkpoints).key, newValue); + expect(await this.contract.latest()).to.be.bignumber.equal(newValue); + + // check that length did not change + expect(await this.contract.length()).to.be.bignumber.equal(this.checkpoints.length.toString()); + }); + + it('lower lookup', async function () { + for (let i = 0; i < 14; ++i) { + const value = first(this.checkpoints.filter(x => i <= x.key))?.value || '0'; + + expect(await this.contract.lowerLookup(i)).to.be.bignumber.equal(value); + } + }); + + it('upper lookup', async function () { + for (let i = 0; i < 14; ++i) { + const value = last(this.checkpoints.filter(x => i >= x.key))?.value || '0'; + + expect(await this.contract.upperLookup(i)).to.be.bignumber.equal(value); + } + }); + }); + }); + } +}); diff --git a/lib/openzeppelin-contracts/test/utils/Context.behavior.js b/lib/openzeppelin-contracts/test/utils/Context.behavior.js new file mode 100644 index 0000000..8728e10 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Context.behavior.js @@ -0,0 +1,42 @@ +const { BN, expectEvent } = require('@openzeppelin/test-helpers'); + +const ContextMock = artifacts.require('ContextMock'); + +function shouldBehaveLikeRegularContext (sender) { + describe('msgSender', function () { + it('returns the transaction sender when called from an EOA', async function () { + const receipt = await this.context.msgSender({ from: sender }); + expectEvent(receipt, 'Sender', { sender }); + }); + + it('returns the transaction sender when from another contract', async function () { + const { tx } = await this.caller.callSender(this.context.address, { from: sender }); + await expectEvent.inTransaction(tx, ContextMock, 'Sender', { sender: this.caller.address }); + }); + }); + + describe('msgData', function () { + const integerValue = new BN('42'); + const stringValue = 'OpenZeppelin'; + + let callData; + + beforeEach(async function () { + callData = this.context.contract.methods.msgData(integerValue.toString(), stringValue).encodeABI(); + }); + + it('returns the transaction data when called from an EOA', async function () { + const receipt = await this.context.msgData(integerValue, stringValue); + expectEvent(receipt, 'Data', { data: callData, integerValue, stringValue }); + }); + + it('returns the transaction sender when from another contract', async function () { + const { tx } = await this.caller.callData(this.context.address, integerValue, stringValue); + await expectEvent.inTransaction(tx, ContextMock, 'Data', { data: callData, integerValue, stringValue }); + }); + }); +} + +module.exports = { + shouldBehaveLikeRegularContext, +}; diff --git a/lib/openzeppelin-contracts/test/utils/Context.test.js b/lib/openzeppelin-contracts/test/utils/Context.test.js new file mode 100644 index 0000000..709aa87 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Context.test.js @@ -0,0 +1,17 @@ +require('@openzeppelin/test-helpers'); + +const ContextMock = artifacts.require('ContextMock'); +const ContextMockCaller = artifacts.require('ContextMockCaller'); + +const { shouldBehaveLikeRegularContext } = require('./Context.behavior'); + +contract('Context', function (accounts) { + const [ sender ] = accounts; + + beforeEach(async function () { + this.context = await ContextMock.new(); + this.caller = await ContextMockCaller.new(); + }); + + shouldBehaveLikeRegularContext(sender); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Counters.test.js b/lib/openzeppelin-contracts/test/utils/Counters.test.js new file mode 100644 index 0000000..04be4c0 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Counters.test.js @@ -0,0 +1,84 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const CountersImpl = artifacts.require('CountersImpl'); + +contract('Counters', function (accounts) { + beforeEach(async function () { + this.counter = await CountersImpl.new(); + }); + + it('starts at zero', async function () { + expect(await this.counter.current()).to.be.bignumber.equal('0'); + }); + + describe('increment', function () { + context('starting from 0', function () { + it('increments the current value by one', async function () { + await this.counter.increment(); + expect(await this.counter.current()).to.be.bignumber.equal('1'); + }); + + it('can be called multiple times', async function () { + await this.counter.increment(); + await this.counter.increment(); + await this.counter.increment(); + + expect(await this.counter.current()).to.be.bignumber.equal('3'); + }); + }); + }); + + describe('decrement', function () { + beforeEach(async function () { + await this.counter.increment(); + expect(await this.counter.current()).to.be.bignumber.equal('1'); + }); + context('starting from 1', function () { + it('decrements the current value by one', async function () { + await this.counter.decrement(); + expect(await this.counter.current()).to.be.bignumber.equal('0'); + }); + + it('reverts if the current value is 0', async function () { + await this.counter.decrement(); + await expectRevert(this.counter.decrement(), 'Counter: decrement overflow'); + }); + }); + context('after incremented to 3', function () { + it('can be called multiple times', async function () { + await this.counter.increment(); + await this.counter.increment(); + + expect(await this.counter.current()).to.be.bignumber.equal('3'); + + await this.counter.decrement(); + await this.counter.decrement(); + await this.counter.decrement(); + + expect(await this.counter.current()).to.be.bignumber.equal('0'); + }); + }); + }); + + describe('reset', function () { + context('null counter', function () { + it('does not throw', async function () { + await this.counter.reset(); + expect(await this.counter.current()).to.be.bignumber.equal('0'); + }); + }); + + context('non null counter', function () { + beforeEach(async function () { + await this.counter.increment(); + expect(await this.counter.current()).to.be.bignumber.equal('1'); + }); + it('reset to 0', async function () { + await this.counter.reset(); + expect(await this.counter.current()).to.be.bignumber.equal('0'); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Create2.test.js b/lib/openzeppelin-contracts/test/utils/Create2.test.js new file mode 100644 index 0000000..b57e063 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Create2.test.js @@ -0,0 +1,92 @@ +const { balance, BN, ether, expectRevert, send } = require('@openzeppelin/test-helpers'); +const { computeCreate2Address } = require('../helpers/create2'); +const { expect } = require('chai'); + +const Create2Impl = artifacts.require('Create2Impl'); +const ERC20Mock = artifacts.require('ERC20Mock'); +const ERC1820Implementer = artifacts.require('ERC1820Implementer'); + +contract('Create2', function (accounts) { + const [deployerAccount] = accounts; + + const salt = 'salt message'; + const saltHex = web3.utils.soliditySha3(salt); + + const encodedParams = web3.eth.abi.encodeParameters( + ['string', 'string', 'address', 'uint256'], + ['MyToken', 'MTKN', deployerAccount, 100], + ).slice(2); + + const constructorByteCode = `${ERC20Mock.bytecode}${encodedParams}`; + + beforeEach(async function () { + this.factory = await Create2Impl.new(); + }); + describe('computeAddress', function () { + it('computes the correct contract address', async function () { + const onChainComputed = await this.factory + .computeAddress(saltHex, web3.utils.keccak256(constructorByteCode)); + const offChainComputed = + computeCreate2Address(saltHex, constructorByteCode, this.factory.address); + expect(onChainComputed).to.equal(offChainComputed); + }); + + it('computes the correct contract address with deployer', async function () { + const onChainComputed = await this.factory + .computeAddressWithDeployer(saltHex, web3.utils.keccak256(constructorByteCode), deployerAccount); + const offChainComputed = + computeCreate2Address(saltHex, constructorByteCode, deployerAccount); + expect(onChainComputed).to.equal(offChainComputed); + }); + }); + + describe('deploy', function () { + it('deploys a ERC1820Implementer from inline assembly code', async function () { + const offChainComputed = + computeCreate2Address(saltHex, ERC1820Implementer.bytecode, this.factory.address); + await this.factory.deployERC1820Implementer(0, saltHex); + expect(ERC1820Implementer.bytecode).to.include((await web3.eth.getCode(offChainComputed)).slice(2)); + }); + + it('deploys a ERC20Mock with correct balances', async function () { + const offChainComputed = computeCreate2Address(saltHex, constructorByteCode, this.factory.address); + + await this.factory.deploy(0, saltHex, constructorByteCode); + + const erc20 = await ERC20Mock.at(offChainComputed); + expect(await erc20.balanceOf(deployerAccount)).to.be.bignumber.equal(new BN(100)); + }); + + it('deploys a contract with funds deposited in the factory', async function () { + const deposit = ether('2'); + await send.ether(deployerAccount, this.factory.address, deposit); + expect(await balance.current(this.factory.address)).to.be.bignumber.equal(deposit); + + const onChainComputed = await this.factory + .computeAddressWithDeployer(saltHex, web3.utils.keccak256(constructorByteCode), this.factory.address); + + await this.factory.deploy(deposit, saltHex, constructorByteCode); + expect(await balance.current(onChainComputed)).to.be.bignumber.equal(deposit); + }); + + it('fails deploying a contract in an existent address', async function () { + await this.factory.deploy(0, saltHex, constructorByteCode, { from: deployerAccount }); + await expectRevert( + this.factory.deploy(0, saltHex, constructorByteCode, { from: deployerAccount }), 'Create2: Failed on deploy', + ); + }); + + it('fails deploying a contract if the bytecode length is zero', async function () { + await expectRevert( + this.factory.deploy(0, saltHex, '0x', { from: deployerAccount }), 'Create2: bytecode length is zero', + ); + }); + + it('fails deploying a contract if factory contract does not have sufficient balance', async function () { + await expectRevert( + this.factory.deploy(1, saltHex, constructorByteCode, { from: deployerAccount }), + 'Create2: insufficient balance', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Multicall.test.js b/lib/openzeppelin-contracts/test/utils/Multicall.test.js new file mode 100644 index 0000000..61c2634 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Multicall.test.js @@ -0,0 +1,57 @@ +const { BN, expectRevert } = require('@openzeppelin/test-helpers'); +const MulticallTokenMock = artifacts.require('MulticallTokenMock'); + +contract('MulticallToken', function (accounts) { + const [deployer, alice, bob] = accounts; + const amount = 12000; + + beforeEach(async function () { + this.multicallToken = await MulticallTokenMock.new(new BN(amount), { from: deployer }); + }); + + it('batches function calls', async function () { + expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN('0')); + expect(await this.multicallToken.balanceOf(bob)).to.be.bignumber.equal(new BN('0')); + + await this.multicallToken.multicall([ + this.multicallToken.contract.methods.transfer(alice, amount / 2).encodeABI(), + this.multicallToken.contract.methods.transfer(bob, amount / 3).encodeABI(), + ], { from: deployer }); + + expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN(amount / 2)); + expect(await this.multicallToken.balanceOf(bob)).to.be.bignumber.equal(new BN(amount / 3)); + }); + + it('returns an array with the result of each call', async function () { + const MulticallTest = artifacts.require('MulticallTest'); + const multicallTest = await MulticallTest.new({ from: deployer }); + await this.multicallToken.transfer(multicallTest.address, amount, { from: deployer }); + expect(await this.multicallToken.balanceOf(multicallTest.address)).to.be.bignumber.equal(new BN(amount)); + + const recipients = [alice, bob]; + const amounts = [amount / 2, amount / 3].map(n => new BN(n)); + + await multicallTest.checkReturnValues(this.multicallToken.address, recipients, amounts); + }); + + it('reverts previous calls', async function () { + expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN('0')); + + const call = this.multicallToken.multicall([ + this.multicallToken.contract.methods.transfer(alice, amount).encodeABI(), + this.multicallToken.contract.methods.transfer(bob, amount).encodeABI(), + ], { from: deployer }); + + await expectRevert(call, 'ERC20: transfer amount exceeds balance'); + expect(await this.multicallToken.balanceOf(alice)).to.be.bignumber.equal(new BN('0')); + }); + + it('bubbles up revert reasons', async function () { + const call = this.multicallToken.multicall([ + this.multicallToken.contract.methods.transfer(alice, amount).encodeABI(), + this.multicallToken.contract.methods.transfer(bob, amount).encodeABI(), + ], { from: deployer }); + + await expectRevert(call, 'ERC20: transfer amount exceeds balance'); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/StorageSlot.test.js b/lib/openzeppelin-contracts/test/utils/StorageSlot.test.js new file mode 100644 index 0000000..9d42887 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/StorageSlot.test.js @@ -0,0 +1,110 @@ +const { constants, BN } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const StorageSlotMock = artifacts.require('StorageSlotMock'); + +const slot = web3.utils.keccak256('some.storage.slot'); +const otherSlot = web3.utils.keccak256('some.other.storage.slot'); + +contract('StorageSlot', function (accounts) { + beforeEach(async function () { + this.store = await StorageSlotMock.new(); + }); + + describe('boolean storage slot', function () { + beforeEach(async function () { + this.value = true; + }); + + it('set', async function () { + await this.store.setBoolean(slot, this.value); + }); + + describe('get', function () { + beforeEach(async function () { + await this.store.setBoolean(slot, this.value); + }); + + it('from right slot', async function () { + expect(await this.store.getBoolean(slot)).to.be.equal(this.value); + }); + + it('from other slot', async function () { + expect(await this.store.getBoolean(otherSlot)).to.be.equal(false); + }); + }); + }); + + describe('address storage slot', function () { + beforeEach(async function () { + this.value = accounts[1]; + }); + + it('set', async function () { + await this.store.setAddress(slot, this.value); + }); + + describe('get', function () { + beforeEach(async function () { + await this.store.setAddress(slot, this.value); + }); + + it('from right slot', async function () { + expect(await this.store.getAddress(slot)).to.be.equal(this.value); + }); + + it('from other slot', async function () { + expect(await this.store.getAddress(otherSlot)).to.be.equal(constants.ZERO_ADDRESS); + }); + }); + }); + + describe('bytes32 storage slot', function () { + beforeEach(async function () { + this.value = web3.utils.keccak256('some byte32 value'); + }); + + it('set', async function () { + await this.store.setBytes32(slot, this.value); + }); + + describe('get', function () { + beforeEach(async function () { + await this.store.setBytes32(slot, this.value); + }); + + it('from right slot', async function () { + expect(await this.store.getBytes32(slot)).to.be.equal(this.value); + }); + + it('from other slot', async function () { + expect(await this.store.getBytes32(otherSlot)).to.be.equal(constants.ZERO_BYTES32); + }); + }); + }); + + describe('uint256 storage slot', function () { + beforeEach(async function () { + this.value = new BN(1742); + }); + + it('set', async function () { + await this.store.setUint256(slot, this.value); + }); + + describe('get', function () { + beforeEach(async function () { + await this.store.setUint256(slot, this.value); + }); + + it('from right slot', async function () { + expect(await this.store.getUint256(slot)).to.be.bignumber.equal(this.value); + }); + + it('from other slot', async function () { + expect(await this.store.getUint256(otherSlot)).to.be.bignumber.equal('0'); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/Strings.test.js b/lib/openzeppelin-contracts/test/utils/Strings.test.js new file mode 100644 index 0000000..7abc859 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/Strings.test.js @@ -0,0 +1,86 @@ +const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const StringsMock = artifacts.require('StringsMock'); + +contract('Strings', function (accounts) { + before(async function () { + this.strings = await StringsMock.new(); + }); + + describe('toString', function () { + for (const [ key, value ] of Object.entries([ + '0', + '7', + '10', + '99', + '100', + '101', + '123', + '4132', + '12345', + '1234567', + '1234567890', + '123456789012345', + '12345678901234567890', + '123456789012345678901234567890', + '1234567890123456789012345678901234567890', + '12345678901234567890123456789012345678901234567890', + '123456789012345678901234567890123456789012345678901234567890', + '1234567890123456789012345678901234567890123456789012345678901234567890', + ].reduce((acc, value) => Object.assign(acc, { [value]: new BN(value) }), { + MAX_UINT256: constants.MAX_UINT256.toString(), + }))) { + it(`converts ${key}`, async function () { + expect(await this.strings.methods['toString(uint256)'](value)).to.equal(value.toString(10)); + }); + } + }); + + describe('toHexString', function () { + it('converts 0', async function () { + expect(await this.strings.methods['toHexString(uint256)'](0)).to.equal('0x00'); + }); + + it('converts a positive number', async function () { + expect(await this.strings.methods['toHexString(uint256)'](0x4132)).to.equal('0x4132'); + }); + + it('converts MAX_UINT256', async function () { + expect(await this.strings.methods['toHexString(uint256)'](constants.MAX_UINT256)) + .to.equal(web3.utils.toHex(constants.MAX_UINT256)); + }); + }); + + describe('toHexString fixed', function () { + it('converts a positive number (long)', async function () { + expect(await this.strings.methods['toHexString(uint256,uint256)'](0x4132, 32)) + .to.equal('0x0000000000000000000000000000000000000000000000000000000000004132'); + }); + + it('converts a positive number (short)', async function () { + await expectRevert( + this.strings.methods['toHexString(uint256,uint256)'](0x4132, 1), + 'Strings: hex length insufficient', + ); + }); + + it('converts MAX_UINT256', async function () { + expect(await this.strings.methods['toHexString(uint256,uint256)'](constants.MAX_UINT256, 32)) + .to.equal(web3.utils.toHex(constants.MAX_UINT256)); + }); + }); + + describe('toHexString address', function () { + it('converts a random address', async function () { + const addr = '0xa9036907dccae6a1e0033479b12e837e5cf5a02f'; + expect(await this.strings.methods['toHexString(address)'](addr)).to.equal(addr); + }); + + it('converts an address with leading zeros', async function () { + const addr = '0x0000e0ca771e21bd00057f54a68c30d400000000'; + expect(await this.strings.methods['toHexString(address)'](addr)).to.equal(addr); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/TimersBlockNumberImpl.test.js b/lib/openzeppelin-contracts/test/utils/TimersBlockNumberImpl.test.js new file mode 100644 index 0000000..d9f83d9 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/TimersBlockNumberImpl.test.js @@ -0,0 +1,55 @@ +const { BN, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const TimersBlockNumberImpl = artifacts.require('TimersBlockNumberImpl'); + +contract('TimersBlockNumber', function (accounts) { + beforeEach(async function () { + this.instance = await TimersBlockNumberImpl.new(); + this.now = await web3.eth.getBlock('latest').then(({ number }) => number); + }); + + it('unset', async function () { + expect(await this.instance.getDeadline()).to.be.bignumber.equal('0'); + expect(await this.instance.isUnset()).to.be.equal(true); + expect(await this.instance.isStarted()).to.be.equal(false); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(false); + }); + + it('pending', async function () { + await this.instance.setDeadline(this.now + 3); + expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now + 3)); + expect(await this.instance.isUnset()).to.be.equal(false); + expect(await this.instance.isStarted()).to.be.equal(true); + expect(await this.instance.isPending()).to.be.equal(true); + expect(await this.instance.isExpired()).to.be.equal(false); + }); + + it('expired', async function () { + await this.instance.setDeadline(this.now - 3); + expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now - 3)); + expect(await this.instance.isUnset()).to.be.equal(false); + expect(await this.instance.isStarted()).to.be.equal(true); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(true); + }); + + it('reset', async function () { + await this.instance.reset(); + expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(0)); + expect(await this.instance.isUnset()).to.be.equal(true); + expect(await this.instance.isStarted()).to.be.equal(false); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(false); + }); + + it('fast forward', async function () { + await this.instance.setDeadline(this.now + 3); + expect(await this.instance.isPending()).to.be.equal(true); + expect(await this.instance.isExpired()).to.be.equal(false); + await time.advanceBlockTo(this.now + 3); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(true); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/TimersTimestamp.test.js b/lib/openzeppelin-contracts/test/utils/TimersTimestamp.test.js new file mode 100644 index 0000000..b08118d --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/TimersTimestamp.test.js @@ -0,0 +1,55 @@ +const { BN, time } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const TimersTimestampImpl = artifacts.require('TimersTimestampImpl'); + +contract('TimersTimestamp', function (accounts) { + beforeEach(async function () { + this.instance = await TimersTimestampImpl.new(); + this.now = await web3.eth.getBlock('latest').then(({ timestamp }) => timestamp); + }); + + it('unset', async function () { + expect(await this.instance.getDeadline()).to.be.bignumber.equal('0'); + expect(await this.instance.isUnset()).to.be.equal(true); + expect(await this.instance.isStarted()).to.be.equal(false); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(false); + }); + + it('pending', async function () { + await this.instance.setDeadline(this.now + 100); + expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now + 100)); + expect(await this.instance.isUnset()).to.be.equal(false); + expect(await this.instance.isStarted()).to.be.equal(true); + expect(await this.instance.isPending()).to.be.equal(true); + expect(await this.instance.isExpired()).to.be.equal(false); + }); + + it('expired', async function () { + await this.instance.setDeadline(this.now - 100); + expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(this.now - 100)); + expect(await this.instance.isUnset()).to.be.equal(false); + expect(await this.instance.isStarted()).to.be.equal(true); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(true); + }); + + it('reset', async function () { + await this.instance.reset(); + expect(await this.instance.getDeadline()).to.be.bignumber.equal(new BN(0)); + expect(await this.instance.isUnset()).to.be.equal(true); + expect(await this.instance.isStarted()).to.be.equal(false); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(false); + }); + + it('fast forward', async function () { + await this.instance.setDeadline(this.now + 100); + expect(await this.instance.isPending()).to.be.equal(true); + expect(await this.instance.isExpired()).to.be.equal(false); + await time.increaseTo(this.now + 100); + expect(await this.instance.isPending()).to.be.equal(false); + expect(await this.instance.isExpired()).to.be.equal(true); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/cryptography/ECDSA.test.js b/lib/openzeppelin-contracts/test/utils/cryptography/ECDSA.test.js new file mode 100644 index 0000000..04374c4 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/cryptography/ECDSA.test.js @@ -0,0 +1,204 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); +const { toEthSignedMessageHash } = require('../../helpers/sign'); + +const { expect } = require('chai'); + +const ECDSAMock = artifacts.require('ECDSAMock'); + +const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin'); +const WRONG_MESSAGE = web3.utils.sha3('Nope'); +const NON_HASH_MESSAGE = '0x' + Buffer.from('abcd').toString('hex'); + +function to2098Format (signature) { + const long = web3.utils.hexToBytes(signature); + if (long.length !== 65) { + throw new Error('invalid signature length (expected long format)'); + } + if (long[32] >> 7 === 1) { + throw new Error('invalid signature \'s\' value'); + } + const short = long.slice(0, 64); + short[32] |= (long[64] % 27) << 7; // set the first bit of the 32nd byte to the v parity bit + return web3.utils.bytesToHex(short); +} + +function split (signature) { + const raw = web3.utils.hexToBytes(signature); + switch (raw.length) { + case 64: + return [ + web3.utils.bytesToHex(raw.slice(0, 32)), // r + web3.utils.bytesToHex(raw.slice(32, 64)), // vs + ]; + case 65: + return [ + raw[64], // v + web3.utils.bytesToHex(raw.slice(0, 32)), // r + web3.utils.bytesToHex(raw.slice(32, 64)), // s + ]; + default: + expect.fail('Invalid signature length, cannot split'); + } +} + +contract('ECDSA', function (accounts) { + const [ other ] = accounts; + + beforeEach(async function () { + this.ecdsa = await ECDSAMock.new(); + }); + + context('recover with invalid signature', function () { + it('with short signature', async function () { + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, '0x1234'), 'ECDSA: invalid signature length'); + }); + + it('with long signature', async function () { + await expectRevert( + // eslint-disable-next-line max-len + this.ecdsa.recover(TEST_MESSAGE, '0x01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'), + 'ECDSA: invalid signature length', + ); + }); + }); + + context('recover with valid signature', function () { + context('using web3.eth.sign', function () { + it('returns signer address with correct signature', async function () { + // Create the signature + const signature = await web3.eth.sign(TEST_MESSAGE, other); + + // Recover the signer address from the generated message and signature. + expect(await this.ecdsa.recover( + toEthSignedMessageHash(TEST_MESSAGE), + signature, + )).to.equal(other); + }); + + it('returns signer address with correct signature for arbitrary length message', async function () { + // Create the signature + const signature = await web3.eth.sign(NON_HASH_MESSAGE, other); + + // Recover the signer address from the generated message and signature. + expect(await this.ecdsa.recover( + toEthSignedMessageHash(NON_HASH_MESSAGE), + signature, + )).to.equal(other); + }); + + it('returns a different address', async function () { + const signature = await web3.eth.sign(TEST_MESSAGE, other); + expect(await this.ecdsa.recover(WRONG_MESSAGE, signature)).to.not.equal(other); + }); + + it('reverts with invalid signature', async function () { + // eslint-disable-next-line max-len + const signature = '0x332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c'; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature'); + }); + }); + + context('with v=27 signature', function () { + // Signature generated outside ganache with method web3.eth.sign(signer, message) + const signer = '0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c'; + // eslint-disable-next-line max-len + const signatureWithoutV = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892'; + + it('works with correct v value', async function () { + const v = '1b'; // 27 = 1b. + const signature = signatureWithoutV + v; + expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); + expect(await this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(signature))).to.equal(signer); + expect(await this.ecdsa.recover_r_vs(TEST_MESSAGE, ...split(to2098Format(signature)))).to.equal(signer); + }); + + it('rejects incorrect v value', async function () { + const v = '1c'; // 28 = 1c. + const signature = signatureWithoutV + v; + expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.not.equal(signer); + expect(await this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(signature))).to.not.equal(signer); + expect(await this.ecdsa.recover_r_vs(TEST_MESSAGE, ...split(to2098Format(signature)))).to.not.equal(signer); + }); + + it('reverts wrong v values', async function () { + for (const v of ['00', '01']) { + const signature = signatureWithoutV + v; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature'); + await expectRevert(this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(signature)), 'ECDSA: invalid signature'); + } + }); + + it('rejects short EIP2098 format', async function () { + const v = '1b'; // 27 = 1b. + const signature = signatureWithoutV + v; + await expectRevert( + this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature)), + 'ECDSA: invalid signature length', + ); + }); + }); + + context('with v=28 signature', function () { + const signer = '0x1E318623aB09Fe6de3C9b8672098464Aeda9100E'; + // eslint-disable-next-line max-len + const signatureWithoutV = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0'; + + it('works with correct v value', async function () { + const v = '1c'; // 28 = 1c. + const signature = signatureWithoutV + v; + expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.equal(signer); + expect(await this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(signature))).to.equal(signer); + expect(await this.ecdsa.recover_r_vs(TEST_MESSAGE, ...split(to2098Format(signature)))).to.equal(signer); + }); + + it('rejects incorrect v value', async function () { + const v = '1b'; // 27 = 1b. + const signature = signatureWithoutV + v; + expect(await this.ecdsa.recover(TEST_MESSAGE, signature)).to.not.equal(signer); + expect(await this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(signature))).to.not.equal(signer); + expect(await this.ecdsa.recover_r_vs(TEST_MESSAGE, ...split(to2098Format(signature)))).to.not.equal(signer); + }); + + it('reverts invalid v values', async function () { + for (const v of ['00', '01']) { + const signature = signatureWithoutV + v; + await expectRevert(this.ecdsa.recover(TEST_MESSAGE, signature), 'ECDSA: invalid signature'); + await expectRevert(this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(signature)), 'ECDSA: invalid signature'); + } + }); + + it('rejects short EIP2098 format', async function () { + const v = '1c'; // 27 = 1b. + const signature = signatureWithoutV + v; + await expectRevert( + this.ecdsa.recover(TEST_MESSAGE, to2098Format(signature)), + 'ECDSA: invalid signature length', + ); + }); + }); + + it('reverts with high-s value signature', async function () { + const message = '0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'; + // eslint-disable-next-line max-len + const highSSignature = '0xe742ff452d41413616a5bf43fe15dd88294e983d3d36206c2712f39083d638bde0a0fc89be718fbc1033e1d30d78be1c68081562ed2e97af876f286f3453231d1b'; + await expectRevert(this.ecdsa.recover(message, highSSignature), 'ECDSA: invalid signature \'s\' value'); + await expectRevert( + this.ecdsa.recover_v_r_s(TEST_MESSAGE, ...split(highSSignature)), + 'ECDSA: invalid signature \'s\' value', + ); + expect(() => to2098Format(highSSignature)).to.throw('invalid signature \'s\' value'); + }); + }); + + context('toEthSignedMessageHash', function () { + it('prefixes bytes32 data correctly', async function () { + expect(await this.ecdsa.methods['toEthSignedMessageHash(bytes32)'](TEST_MESSAGE)) + .to.equal(toEthSignedMessageHash(TEST_MESSAGE)); + }); + + it('prefixes dynamic length data correctly', async function () { + expect(await this.ecdsa.methods['toEthSignedMessageHash(bytes)'](NON_HASH_MESSAGE)) + .to.equal(toEthSignedMessageHash(NON_HASH_MESSAGE)); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/cryptography/EIP712.test.js b/lib/openzeppelin-contracts/test/utils/cryptography/EIP712.test.js new file mode 100644 index 0000000..9e26a87 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/cryptography/EIP712.test.js @@ -0,0 +1,57 @@ +const ethSigUtil = require('eth-sig-util'); +const Wallet = require('ethereumjs-wallet').default; + +const { EIP712Domain, domainSeparator } = require('../../helpers/eip712'); + +const EIP712 = artifacts.require('EIP712External'); + +contract('EIP712', function (accounts) { + const [mailTo] = accounts; + + const name = 'A Name'; + const version = '1'; + + beforeEach('deploying', async function () { + this.eip712 = await EIP712.new(name, version); + + // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id + // from within the EVM as from the JSON RPC interface. + // See https://github.com/trufflesuite/ganache-core/issues/515 + this.chainId = await this.eip712.getChainId(); + }); + + it('domain separator', async function () { + expect( + await this.eip712.domainSeparator(), + ).to.equal( + await domainSeparator(name, version, this.chainId, this.eip712.address), + ); + }); + + it('digest', async function () { + const chainId = this.chainId; + const verifyingContract = this.eip712.address; + const message = { + to: mailTo, + contents: 'very interesting', + }; + + const data = { + types: { + EIP712Domain, + Mail: [ + { name: 'to', type: 'address' }, + { name: 'contents', type: 'string' }, + ], + }, + domain: { name, version, chainId, verifyingContract }, + primaryType: 'Mail', + message, + }; + + const wallet = Wallet.generate(); + const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }); + + await this.eip712.verify(signature, wallet.getAddressString(), message.to, message.contents); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/cryptography/MerkleProof.test.js b/lib/openzeppelin-contracts/test/utils/cryptography/MerkleProof.test.js new file mode 100644 index 0000000..2d4aacd --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/cryptography/MerkleProof.test.js @@ -0,0 +1,179 @@ +require('@openzeppelin/test-helpers'); + +const { expectRevert } = require('@openzeppelin/test-helpers'); +const { MerkleTree } = require('merkletreejs'); +const keccak256 = require('keccak256'); + +const { expect } = require('chai'); + +const MerkleProofWrapper = artifacts.require('MerkleProofWrapper'); + +contract('MerkleProof', function (accounts) { + beforeEach(async function () { + this.merkleProof = await MerkleProofWrapper.new(); + }); + + describe('verify', function () { + it('returns true for a valid Merkle proof', async function () { + const elements = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''); + const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true, sortPairs: true }); + + const root = merkleTree.getHexRoot(); + + const leaf = keccak256(elements[0]); + + const proof = merkleTree.getHexProof(leaf); + + expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true); + expect(await this.merkleProof.verifyCalldata(proof, root, leaf)).to.equal(true); + + // For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements: + const noSuchLeaf = keccak256( + Buffer.concat([keccak256(elements[0]), keccak256(elements[1])].sort(Buffer.compare)), + ); + expect(await this.merkleProof.verify(proof.slice(1), root, noSuchLeaf)).to.equal(true); + expect(await this.merkleProof.verifyCalldata(proof.slice(1), root, noSuchLeaf)).to.equal(true); + }); + + it('returns false for an invalid Merkle proof', async function () { + const correctElements = ['a', 'b', 'c']; + const correctMerkleTree = new MerkleTree(correctElements, keccak256, { hashLeaves: true, sortPairs: true }); + + const correctRoot = correctMerkleTree.getHexRoot(); + + const correctLeaf = keccak256(correctElements[0]); + + const badElements = ['d', 'e', 'f']; + const badMerkleTree = new MerkleTree(badElements); + + const badProof = badMerkleTree.getHexProof(badElements[0]); + + expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false); + expect(await this.merkleProof.verifyCalldata(badProof, correctRoot, correctLeaf)).to.equal(false); + }); + + it('returns false for a Merkle proof of invalid length', async function () { + const elements = ['a', 'b', 'c']; + const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true, sortPairs: true }); + + const root = merkleTree.getHexRoot(); + + const leaf = keccak256(elements[0]); + + const proof = merkleTree.getHexProof(leaf); + const badProof = proof.slice(0, proof.length - 5); + + expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false); + expect(await this.merkleProof.verifyCalldata(badProof, root, leaf)).to.equal(false); + }); + }); + + describe('multiProofVerify', function () { + it('returns true for a valid Merkle multi proof', async function () { + const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(keccak256).sort(Buffer.compare); + const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); + + const root = merkleTree.getRoot(); + const proofLeaves = ['b', 'f', 'd'].map(keccak256).sort(Buffer.compare); + const proof = merkleTree.getMultiProof(proofLeaves); + const proofFlags = merkleTree.getProofFlags(proofLeaves, proof); + + expect(await this.merkleProof.multiProofVerify(proof, proofFlags, root, proofLeaves)).to.equal(true); + expect(await this.merkleProof.multiProofVerifyCalldata(proof, proofFlags, root, proofLeaves)).to.equal(true); + }); + + it('returns false for an invalid Merkle multi proof', async function () { + const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(keccak256).sort(Buffer.compare); + const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); + + const root = merkleTree.getRoot(); + const badProofLeaves = ['g', 'h', 'i'].map(keccak256).sort(Buffer.compare); + const badMerkleTree = new MerkleTree(badProofLeaves); + const badProof = badMerkleTree.getMultiProof(badProofLeaves); + const badProofFlags = badMerkleTree.getProofFlags(badProofLeaves, badProof); + + expect(await this.merkleProof.multiProofVerify(badProof, badProofFlags, root, badProofLeaves)) + .to.equal(false); + expect(await this.merkleProof.multiProofVerifyCalldata(badProof, badProofFlags, root, badProofLeaves)) + .to.equal(false); + }); + + it('revert with invalid multi proof #1', async function () { + const fill = Buffer.alloc(32); // This could be anything, we are reconstructing a fake branch + const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare); + const badLeaf = keccak256('e'); + const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); + + const root = merkleTree.getRoot(); + + await expectRevert( + this.merkleProof.multiProofVerify( + [ leaves[1], fill, merkleTree.layers[1][1] ], + [ false, false, false ], + root, + [ leaves[0], badLeaf ], // A, E + ), + 'MerkleProof: invalid multiproof', + ); + await expectRevert( + this.merkleProof.multiProofVerifyCalldata( + [ leaves[1], fill, merkleTree.layers[1][1] ], + [ false, false, false ], + root, + [ leaves[0], badLeaf ], // A, E + ), + 'MerkleProof: invalid multiproof', + ); + }); + + it('revert with invalid multi proof #2', async function () { + const fill = Buffer.alloc(32); // This could be anything, we are reconstructing a fake branch + const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare); + const badLeaf = keccak256('e'); + const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); + + const root = merkleTree.getRoot(); + + await expectRevert( + this.merkleProof.multiProofVerify( + [ leaves[1], fill, merkleTree.layers[1][1] ], + [ false, false, false, false ], + root, + [ badLeaf, leaves[0] ], // A, E + ), + 'reverted with panic code 0x32', + ); + await expectRevert( + this.merkleProof.multiProofVerifyCalldata( + [ leaves[1], fill, merkleTree.layers[1][1] ], + [ false, false, false, false ], + root, + [ badLeaf, leaves[0] ], // A, E + ), + 'reverted with panic code 0x32', + ); + }); + + it('limit case: works for tree containing a single leaf', async function () { + const leaves = ['a'].map(keccak256).sort(Buffer.compare); + const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); + + const root = merkleTree.getRoot(); + const proofLeaves = ['a'].map(keccak256).sort(Buffer.compare); + const proof = merkleTree.getMultiProof(proofLeaves); + const proofFlags = merkleTree.getProofFlags(proofLeaves, proof); + + expect(await this.merkleProof.multiProofVerify(proof, proofFlags, root, proofLeaves)).to.equal(true); + expect(await this.merkleProof.multiProofVerifyCalldata(proof, proofFlags, root, proofLeaves)).to.equal(true); + }); + + it('limit case: can prove empty leaves', async function () { + const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare); + const merkleTree = new MerkleTree(leaves, keccak256, { sort: true }); + + const root = merkleTree.getRoot(); + expect(await this.merkleProof.multiProofVerify([ root ], [], root, [])).to.equal(true); + expect(await this.merkleProof.multiProofVerifyCalldata([ root ], [], root, [])).to.equal(true); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/cryptography/SignatureChecker.test.js b/lib/openzeppelin-contracts/test/utils/cryptography/SignatureChecker.test.js new file mode 100644 index 0000000..801377a --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/cryptography/SignatureChecker.test.js @@ -0,0 +1,81 @@ +const { toEthSignedMessageHash } = require('../../helpers/sign'); + +const { expect } = require('chai'); + +const SignatureCheckerMock = artifacts.require('SignatureCheckerMock'); +const ERC1271WalletMock = artifacts.require('ERC1271WalletMock'); +const ERC1271MaliciousMock = artifacts.require('ERC1271MaliciousMock'); + +const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin'); +const WRONG_MESSAGE = web3.utils.sha3('Nope'); + +contract('SignatureChecker (ERC1271)', function (accounts) { + const [signer, other] = accounts; + + before('deploying', async function () { + this.signaturechecker = await SignatureCheckerMock.new(); + this.wallet = await ERC1271WalletMock.new(signer); + this.malicious = await ERC1271MaliciousMock.new(); + this.signature = await web3.eth.sign(TEST_MESSAGE, signer); + }); + + context('EOA account', function () { + it('with matching signer and signature', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + signer, + toEthSignedMessageHash(TEST_MESSAGE), + this.signature, + )).to.equal(true); + }); + + it('with invalid signer', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + other, + toEthSignedMessageHash(TEST_MESSAGE), + this.signature, + )).to.equal(false); + }); + + it('with invalid signature', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + signer, + toEthSignedMessageHash(WRONG_MESSAGE), + this.signature, + )).to.equal(false); + }); + }); + + context('ERC1271 wallet', function () { + it('with matching signer and signature', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + this.wallet.address, + toEthSignedMessageHash(TEST_MESSAGE), + this.signature, + )).to.equal(true); + }); + + it('with invalid signer', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + this.signaturechecker.address, + toEthSignedMessageHash(TEST_MESSAGE), + this.signature, + )).to.equal(false); + }); + + it('with invalid signature', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + this.wallet.address, + toEthSignedMessageHash(WRONG_MESSAGE), + this.signature, + )).to.equal(false); + }); + + it('with malicious wallet', async function () { + expect(await this.signaturechecker.isValidSignatureNow( + this.malicious.address, + toEthSignedMessageHash(TEST_MESSAGE), + this.signature, + )).to.equal(false); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/escrow/ConditionalEscrow.test.js b/lib/openzeppelin-contracts/test/utils/escrow/ConditionalEscrow.test.js new file mode 100644 index 0000000..3386ca5 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/escrow/ConditionalEscrow.test.js @@ -0,0 +1,36 @@ +const { ether, expectRevert } = require('@openzeppelin/test-helpers'); +const { shouldBehaveLikeEscrow } = require('./Escrow.behavior'); + +const ConditionalEscrowMock = artifacts.require('ConditionalEscrowMock'); + +contract('ConditionalEscrow', function (accounts) { + const [ owner, payee, ...otherAccounts ] = accounts; + + beforeEach(async function () { + this.escrow = await ConditionalEscrowMock.new({ from: owner }); + }); + + context('when withdrawal is allowed', function () { + beforeEach(async function () { + await Promise.all(otherAccounts.map(payee => this.escrow.setAllowed(payee, true))); + }); + + shouldBehaveLikeEscrow(owner, otherAccounts); + }); + + context('when withdrawal is disallowed', function () { + const amount = ether('23'); + + beforeEach(async function () { + await this.escrow.setAllowed(payee, false); + }); + + it('reverts on withdrawals', async function () { + await this.escrow.deposit(payee, { from: owner, value: amount }); + + await expectRevert(this.escrow.withdraw(payee, { from: owner }), + 'ConditionalEscrow: payee is not allowed to withdraw', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/escrow/Escrow.behavior.js b/lib/openzeppelin-contracts/test/utils/escrow/Escrow.behavior.js new file mode 100644 index 0000000..ab59059 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/escrow/Escrow.behavior.js @@ -0,0 +1,94 @@ +const { balance, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +function shouldBehaveLikeEscrow (owner, [payee1, payee2]) { + const amount = ether('42'); + + describe('as an escrow', function () { + describe('deposits', function () { + it('can accept a single deposit', async function () { + await this.escrow.deposit(payee1, { from: owner, value: amount }); + + expect(await balance.current(this.escrow.address)).to.be.bignumber.equal(amount); + + expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal(amount); + }); + + it('can accept an empty deposit', async function () { + await this.escrow.deposit(payee1, { from: owner, value: 0 }); + }); + + it('only the owner can deposit', async function () { + await expectRevert(this.escrow.deposit(payee1, { from: payee2 }), + 'Ownable: caller is not the owner', + ); + }); + + it('emits a deposited event', async function () { + const receipt = await this.escrow.deposit(payee1, { from: owner, value: amount }); + expectEvent(receipt, 'Deposited', { + payee: payee1, + weiAmount: amount, + }); + }); + + it('can add multiple deposits on a single account', async function () { + await this.escrow.deposit(payee1, { from: owner, value: amount }); + await this.escrow.deposit(payee1, { from: owner, value: amount.muln(2) }); + + expect(await balance.current(this.escrow.address)).to.be.bignumber.equal(amount.muln(3)); + + expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal(amount.muln(3)); + }); + + it('can track deposits to multiple accounts', async function () { + await this.escrow.deposit(payee1, { from: owner, value: amount }); + await this.escrow.deposit(payee2, { from: owner, value: amount.muln(2) }); + + expect(await balance.current(this.escrow.address)).to.be.bignumber.equal(amount.muln(3)); + + expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal(amount); + + expect(await this.escrow.depositsOf(payee2)).to.be.bignumber.equal(amount.muln(2)); + }); + }); + + describe('withdrawals', async function () { + it('can withdraw payments', async function () { + const balanceTracker = await balance.tracker(payee1); + + await this.escrow.deposit(payee1, { from: owner, value: amount }); + await this.escrow.withdraw(payee1, { from: owner }); + + expect(await balanceTracker.delta()).to.be.bignumber.equal(amount); + + expect(await balance.current(this.escrow.address)).to.be.bignumber.equal('0'); + expect(await this.escrow.depositsOf(payee1)).to.be.bignumber.equal('0'); + }); + + it('can do an empty withdrawal', async function () { + await this.escrow.withdraw(payee1, { from: owner }); + }); + + it('only the owner can withdraw', async function () { + await expectRevert(this.escrow.withdraw(payee1, { from: payee1 }), + 'Ownable: caller is not the owner', + ); + }); + + it('emits a withdrawn event', async function () { + await this.escrow.deposit(payee1, { from: owner, value: amount }); + const receipt = await this.escrow.withdraw(payee1, { from: owner }); + expectEvent(receipt, 'Withdrawn', { + payee: payee1, + weiAmount: amount, + }); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeEscrow, +}; diff --git a/lib/openzeppelin-contracts/test/utils/escrow/Escrow.test.js b/lib/openzeppelin-contracts/test/utils/escrow/Escrow.test.js new file mode 100644 index 0000000..025a2a9 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/escrow/Escrow.test.js @@ -0,0 +1,14 @@ +require('@openzeppelin/test-helpers'); +const { shouldBehaveLikeEscrow } = require('./Escrow.behavior'); + +const Escrow = artifacts.require('Escrow'); + +contract('Escrow', function (accounts) { + const [ owner, ...otherAccounts ] = accounts; + + beforeEach(async function () { + this.escrow = await Escrow.new({ from: owner }); + }); + + shouldBehaveLikeEscrow(owner, otherAccounts); +}); diff --git a/lib/openzeppelin-contracts/test/utils/escrow/RefundEscrow.test.js b/lib/openzeppelin-contracts/test/utils/escrow/RefundEscrow.test.js new file mode 100644 index 0000000..26c19b3 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/escrow/RefundEscrow.test.js @@ -0,0 +1,148 @@ +const { balance, constants, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { ZERO_ADDRESS } = constants; + +const { expect } = require('chai'); + +const RefundEscrow = artifacts.require('RefundEscrow'); + +contract('RefundEscrow', function (accounts) { + const [ owner, beneficiary, refundee1, refundee2 ] = accounts; + + const amount = ether('54'); + const refundees = [refundee1, refundee2]; + + it('requires a non-null beneficiary', async function () { + await expectRevert( + RefundEscrow.new(ZERO_ADDRESS, { from: owner }), 'RefundEscrow: beneficiary is the zero address', + ); + }); + + context('once deployed', function () { + beforeEach(async function () { + this.escrow = await RefundEscrow.new(beneficiary, { from: owner }); + }); + + context('active state', function () { + it('has beneficiary and state', async function () { + expect(await this.escrow.beneficiary()).to.equal(beneficiary); + expect(await this.escrow.state()).to.be.bignumber.equal('0'); + }); + + it('accepts deposits', async function () { + await this.escrow.deposit(refundee1, { from: owner, value: amount }); + + expect(await this.escrow.depositsOf(refundee1)).to.be.bignumber.equal(amount); + }); + + it('does not refund refundees', async function () { + await this.escrow.deposit(refundee1, { from: owner, value: amount }); + await expectRevert(this.escrow.withdraw(refundee1), + 'ConditionalEscrow: payee is not allowed to withdraw', + ); + }); + + it('does not allow beneficiary withdrawal', async function () { + await this.escrow.deposit(refundee1, { from: owner, value: amount }); + await expectRevert(this.escrow.beneficiaryWithdraw(), + 'RefundEscrow: beneficiary can only withdraw while closed', + ); + }); + }); + + it('only the owner can enter closed state', async function () { + await expectRevert(this.escrow.close({ from: beneficiary }), + 'Ownable: caller is not the owner', + ); + + const receipt = await this.escrow.close({ from: owner }); + expectEvent(receipt, 'RefundsClosed'); + }); + + context('closed state', function () { + beforeEach(async function () { + await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); + + await this.escrow.close({ from: owner }); + }); + + it('rejects deposits', async function () { + await expectRevert(this.escrow.deposit(refundee1, { from: owner, value: amount }), + 'RefundEscrow: can only deposit while active', + ); + }); + + it('does not refund refundees', async function () { + await expectRevert(this.escrow.withdraw(refundee1), + 'ConditionalEscrow: payee is not allowed to withdraw', + ); + }); + + it('allows beneficiary withdrawal', async function () { + const balanceTracker = await balance.tracker(beneficiary); + await this.escrow.beneficiaryWithdraw(); + expect(await balanceTracker.delta()).to.be.bignumber.equal(amount.muln(refundees.length)); + }); + + it('prevents entering the refund state', async function () { + await expectRevert(this.escrow.enableRefunds({ from: owner }), + 'RefundEscrow: can only enable refunds while active', + ); + }); + + it('prevents re-entering the closed state', async function () { + await expectRevert(this.escrow.close({ from: owner }), + 'RefundEscrow: can only close while active', + ); + }); + }); + + it('only the owner can enter refund state', async function () { + await expectRevert(this.escrow.enableRefunds({ from: beneficiary }), + 'Ownable: caller is not the owner', + ); + + const receipt = await this.escrow.enableRefunds({ from: owner }); + expectEvent(receipt, 'RefundsEnabled'); + }); + + context('refund state', function () { + beforeEach(async function () { + await Promise.all(refundees.map(refundee => this.escrow.deposit(refundee, { from: owner, value: amount }))); + + await this.escrow.enableRefunds({ from: owner }); + }); + + it('rejects deposits', async function () { + await expectRevert(this.escrow.deposit(refundee1, { from: owner, value: amount }), + 'RefundEscrow: can only deposit while active', + ); + }); + + it('refunds refundees', async function () { + for (const refundee of [refundee1, refundee2]) { + const balanceTracker = await balance.tracker(refundee); + await this.escrow.withdraw(refundee, { from: owner }); + expect(await balanceTracker.delta()).to.be.bignumber.equal(amount); + } + }); + + it('does not allow beneficiary withdrawal', async function () { + await expectRevert(this.escrow.beneficiaryWithdraw(), + 'RefundEscrow: beneficiary can only withdraw while closed', + ); + }); + + it('prevents entering the closed state', async function () { + await expectRevert(this.escrow.close({ from: owner }), + 'RefundEscrow: can only close while active', + ); + }); + + it('prevents re-entering the refund state', async function () { + await expectRevert(this.escrow.enableRefunds({ from: owner }), + 'RefundEscrow: can only enable refunds while active', + ); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/introspection/ERC165.test.js b/lib/openzeppelin-contracts/test/utils/introspection/ERC165.test.js new file mode 100644 index 0000000..c891500 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/introspection/ERC165.test.js @@ -0,0 +1,13 @@ +const { shouldSupportInterfaces } = require('./SupportsInterface.behavior'); + +const ERC165Mock = artifacts.require('ERC165Mock'); + +contract('ERC165', function (accounts) { + beforeEach(async function () { + this.mock = await ERC165Mock.new(); + }); + + shouldSupportInterfaces([ + 'ERC165', + ]); +}); diff --git a/lib/openzeppelin-contracts/test/utils/introspection/ERC165Checker.test.js b/lib/openzeppelin-contracts/test/utils/introspection/ERC165Checker.test.js new file mode 100644 index 0000000..90409a8 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/introspection/ERC165Checker.test.js @@ -0,0 +1,303 @@ +require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const ERC165CheckerMock = artifacts.require('ERC165CheckerMock'); +const ERC165MissingData = artifacts.require('ERC165MissingData'); +const ERC165MaliciousData = artifacts.require('ERC165MaliciousData'); +const ERC165NotSupported = artifacts.require('ERC165NotSupported'); +const ERC165InterfacesSupported = artifacts.require('ERC165InterfacesSupported'); +const ERC165ReturnBombMock = artifacts.require('ERC165ReturnBombMock'); + +const DUMMY_ID = '0xdeadbeef'; +const DUMMY_ID_2 = '0xcafebabe'; +const DUMMY_ID_3 = '0xdecafbad'; +const DUMMY_UNSUPPORTED_ID = '0xbaddcafe'; +const DUMMY_UNSUPPORTED_ID_2 = '0xbaadcafe'; +const DUMMY_ACCOUNT = '0x1111111111111111111111111111111111111111'; + +contract('ERC165Checker', function (accounts) { + beforeEach(async function () { + this.mock = await ERC165CheckerMock.new(); + }); + + context('ERC165 missing return data', function () { + beforeEach(async function () { + this.target = await ERC165MissingData.new(); + }); + + it('does not support ERC165', async function () { + const supported = await this.mock.supportsERC165(this.target.address); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsInterface', async function () { + const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]); + expect(supported.length).to.equal(1); + expect(supported[0]).to.equal(false); + }); + + it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () { + const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + }); + + context('ERC165 malicious return data', function () { + beforeEach(async function () { + this.target = await ERC165MaliciousData.new(); + }); + + it('does not support ERC165', async function () { + const supported = await this.mock.supportsERC165(this.target.address); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsInterface', async function () { + const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]); + expect(supported.length).to.equal(1); + expect(supported[0]).to.equal(false); + }); + + it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () { + const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID); + expect(supported).to.equal(true); + }); + }); + + context('ERC165 not supported', function () { + beforeEach(async function () { + this.target = await ERC165NotSupported.new(); + }); + + it('does not support ERC165', async function () { + const supported = await this.mock.supportsERC165(this.target.address); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsInterface', async function () { + const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]); + expect(supported.length).to.equal(1); + expect(supported[0]).to.equal(false); + }); + + it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () { + const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + }); + + context('ERC165 supported', function () { + beforeEach(async function () { + this.target = await ERC165InterfacesSupported.new([]); + }); + + it('supports ERC165', async function () { + const supported = await this.mock.supportsERC165(this.target.address); + expect(supported).to.equal(true); + }); + + it('does not support mock interface via supportsInterface', async function () { + const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]); + expect(supported.length).to.equal(1); + expect(supported[0]).to.equal(false); + }); + + it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () { + const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID); + expect(supported).to.equal(false); + }); + }); + + context('ERC165 and single interface supported', function () { + beforeEach(async function () { + this.target = await ERC165InterfacesSupported.new([DUMMY_ID]); + }); + + it('supports ERC165', async function () { + const supported = await this.mock.supportsERC165(this.target.address); + expect(supported).to.equal(true); + }); + + it('supports mock interface via supportsInterface', async function () { + const supported = await this.mock.supportsInterface(this.target.address, DUMMY_ID); + expect(supported).to.equal(true); + }); + + it('supports mock interface via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(this.target.address, [DUMMY_ID]); + expect(supported).to.equal(true); + }); + + it('supports mock interface via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(this.target.address, [DUMMY_ID]); + expect(supported.length).to.equal(1); + expect(supported[0]).to.equal(true); + }); + + it('supports mock interface via supportsERC165InterfaceUnchecked', async function () { + const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, DUMMY_ID); + expect(supported).to.equal(true); + }); + }); + + context('ERC165 and many interfaces supported', function () { + beforeEach(async function () { + this.supportedInterfaces = [DUMMY_ID, DUMMY_ID_2, DUMMY_ID_3]; + this.target = await ERC165InterfacesSupported.new(this.supportedInterfaces); + }); + + it('supports ERC165', async function () { + const supported = await this.mock.supportsERC165(this.target.address); + expect(supported).to.equal(true); + }); + + it('supports each interfaceId via supportsInterface', async function () { + for (const interfaceId of this.supportedInterfaces) { + const supported = await this.mock.supportsInterface(this.target.address, interfaceId); + expect(supported).to.equal(true); + }; + }); + + it('supports all interfaceIds via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(this.target.address, this.supportedInterfaces); + expect(supported).to.equal(true); + }); + + it('supports none of the interfaces queried via supportsAllInterfaces', async function () { + const interfaceIdsToTest = [DUMMY_UNSUPPORTED_ID, DUMMY_UNSUPPORTED_ID_2]; + + const supported = await this.mock.supportsAllInterfaces(this.target.address, interfaceIdsToTest); + expect(supported).to.equal(false); + }); + + it('supports not all of the interfaces queried via supportsAllInterfaces', async function () { + const interfaceIdsToTest = [...this.supportedInterfaces, DUMMY_UNSUPPORTED_ID]; + + const supported = await this.mock.supportsAllInterfaces(this.target.address, interfaceIdsToTest); + expect(supported).to.equal(false); + }); + + it('supports all interfaceIds via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(this.target.address, this.supportedInterfaces); + expect(supported.length).to.equal(3); + expect(supported[0]).to.equal(true); + expect(supported[1]).to.equal(true); + expect(supported[2]).to.equal(true); + }); + + it('supports none of the interfaces queried via getSupportedInterfaces', async function () { + const interfaceIdsToTest = [DUMMY_UNSUPPORTED_ID, DUMMY_UNSUPPORTED_ID_2]; + + const supported = await this.mock.getSupportedInterfaces(this.target.address, interfaceIdsToTest); + expect(supported.length).to.equal(2); + expect(supported[0]).to.equal(false); + expect(supported[1]).to.equal(false); + }); + + it('supports not all of the interfaces queried via getSupportedInterfaces', async function () { + const interfaceIdsToTest = [...this.supportedInterfaces, DUMMY_UNSUPPORTED_ID]; + + const supported = await this.mock.getSupportedInterfaces(this.target.address, interfaceIdsToTest); + expect(supported.length).to.equal(4); + expect(supported[0]).to.equal(true); + expect(supported[1]).to.equal(true); + expect(supported[2]).to.equal(true); + expect(supported[3]).to.equal(false); + }); + + it('supports each interfaceId via supportsERC165InterfaceUnchecked', async function () { + for (const interfaceId of this.supportedInterfaces) { + const supported = await this.mock.supportsERC165InterfaceUnchecked(this.target.address, interfaceId); + expect(supported).to.equal(true); + }; + }); + }); + + context('account address does not support ERC165', function () { + it('does not support ERC165', async function () { + const supported = await this.mock.supportsERC165(DUMMY_ACCOUNT); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsInterface', async function () { + const supported = await this.mock.supportsInterface(DUMMY_ACCOUNT, DUMMY_ID); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via supportsAllInterfaces', async function () { + const supported = await this.mock.supportsAllInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]); + expect(supported).to.equal(false); + }); + + it('does not support mock interface via getSupportedInterfaces', async function () { + const supported = await this.mock.getSupportedInterfaces(DUMMY_ACCOUNT, [DUMMY_ID]); + expect(supported.length).to.equal(1); + expect(supported[0]).to.equal(false); + }); + + it('does not support mock interface via supportsERC165InterfaceUnchecked', async function () { + const supported = await this.mock.supportsERC165InterfaceUnchecked(DUMMY_ACCOUNT, DUMMY_ID); + expect(supported).to.equal(false); + }); + }); + + it('Return bomb resistance', async function () { + this.target = await ERC165ReturnBombMock.new(); + + const tx1 = await this.mock.supportsInterface.sendTransaction(this.target.address, DUMMY_ID); + expect(tx1.receipt.gasUsed).to.be.lessThan(120000); // 3*30k + 21k + some margin + + const tx2 = await this.mock.getSupportedInterfaces.sendTransaction( + this.target.address, + [ + DUMMY_ID, + DUMMY_ID_2, + DUMMY_ID_3, + DUMMY_UNSUPPORTED_ID, + DUMMY_UNSUPPORTED_ID_2, + ], + ); + expect(tx2.receipt.gasUsed).to.be.lessThan(250000); // (2+5)*30k + 21k + some margin + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/introspection/ERC165Storage.test.js b/lib/openzeppelin-contracts/test/utils/introspection/ERC165Storage.test.js new file mode 100644 index 0000000..568d645 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/introspection/ERC165Storage.test.js @@ -0,0 +1,25 @@ +const { expectRevert } = require('@openzeppelin/test-helpers'); + +const { shouldSupportInterfaces } = require('./SupportsInterface.behavior'); + +const ERC165Mock = artifacts.require('ERC165StorageMock'); + +contract('ERC165Storage', function (accounts) { + beforeEach(async function () { + this.mock = await ERC165Mock.new(); + }); + + it('register interface', async function () { + expect(await this.mock.supportsInterface('0x00000001')).to.be.equal(false); + await this.mock.registerInterface('0x00000001'); + expect(await this.mock.supportsInterface('0x00000001')).to.be.equal(true); + }); + + it('does not allow 0xffffffff', async function () { + await expectRevert(this.mock.registerInterface('0xffffffff'), 'ERC165: invalid interface id'); + }); + + shouldSupportInterfaces([ + 'ERC165', + ]); +}); diff --git a/lib/openzeppelin-contracts/test/utils/introspection/ERC1820Implementer.test.js b/lib/openzeppelin-contracts/test/utils/introspection/ERC1820Implementer.test.js new file mode 100644 index 0000000..8d9fe56 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/introspection/ERC1820Implementer.test.js @@ -0,0 +1,66 @@ +const { expectRevert, singletons } = require('@openzeppelin/test-helpers'); +const { bufferToHex, keccakFromString } = require('ethereumjs-util'); + +const { expect } = require('chai'); + +const ERC1820ImplementerMock = artifacts.require('ERC1820ImplementerMock'); + +contract('ERC1820Implementer', function (accounts) { + const [ registryFunder, implementee, other ] = accounts; + + const ERC1820_ACCEPT_MAGIC = bufferToHex(keccakFromString('ERC1820_ACCEPT_MAGIC')); + + beforeEach(async function () { + this.implementer = await ERC1820ImplementerMock.new(); + this.registry = await singletons.ERC1820Registry(registryFunder); + + this.interfaceA = bufferToHex(keccakFromString('interfaceA')); + this.interfaceB = bufferToHex(keccakFromString('interfaceB')); + }); + + context('with no registered interfaces', function () { + it('returns false when interface implementation is queried', async function () { + expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceA, implementee)) + .to.not.equal(ERC1820_ACCEPT_MAGIC); + }); + + it('reverts when attempting to set as implementer in the registry', async function () { + await expectRevert( + this.registry.setInterfaceImplementer( + implementee, this.interfaceA, this.implementer.address, { from: implementee }, + ), + 'Does not implement the interface', + ); + }); + }); + + context('with registered interfaces', function () { + beforeEach(async function () { + await this.implementer.registerInterfaceForAddress(this.interfaceA, implementee); + }); + + it('returns true when interface implementation is queried', async function () { + expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceA, implementee)) + .to.equal(ERC1820_ACCEPT_MAGIC); + }); + + it('returns false when interface implementation for non-supported interfaces is queried', async function () { + expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceB, implementee)) + .to.not.equal(ERC1820_ACCEPT_MAGIC); + }); + + it('returns false when interface implementation for non-supported addresses is queried', async function () { + expect(await this.implementer.canImplementInterfaceForAddress(this.interfaceA, other)) + .to.not.equal(ERC1820_ACCEPT_MAGIC); + }); + + it('can be set as an implementer for supported interfaces in the registry', async function () { + await this.registry.setInterfaceImplementer( + implementee, this.interfaceA, this.implementer.address, { from: implementee }, + ); + + expect(await this.registry.getInterfaceImplementer(implementee, this.interfaceA)) + .to.equal(this.implementer.address); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/introspection/SupportsInterface.behavior.js b/lib/openzeppelin-contracts/test/utils/introspection/SupportsInterface.behavior.js new file mode 100644 index 0000000..78e3272 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/introspection/SupportsInterface.behavior.js @@ -0,0 +1,147 @@ +const { makeInterfaceId } = require('@openzeppelin/test-helpers'); + +const { expect } = require('chai'); + +const INTERFACES = { + ERC165: [ + 'supportsInterface(bytes4)', + ], + ERC721: [ + 'balanceOf(address)', + 'ownerOf(uint256)', + 'approve(address,uint256)', + 'getApproved(uint256)', + 'setApprovalForAll(address,bool)', + 'isApprovedForAll(address,address)', + 'transferFrom(address,address,uint256)', + 'safeTransferFrom(address,address,uint256)', + 'safeTransferFrom(address,address,uint256,bytes)', + ], + ERC721Enumerable: [ + 'totalSupply()', + 'tokenOfOwnerByIndex(address,uint256)', + 'tokenByIndex(uint256)', + ], + ERC721Metadata: [ + 'name()', + 'symbol()', + 'tokenURI(uint256)', + ], + ERC1155: [ + 'balanceOf(address,uint256)', + 'balanceOfBatch(address[],uint256[])', + 'setApprovalForAll(address,bool)', + 'isApprovedForAll(address,address)', + 'safeTransferFrom(address,address,uint256,uint256,bytes)', + 'safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)', + ], + ERC1155Receiver: [ + 'onERC1155Received(address,address,uint256,uint256,bytes)', + 'onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)', + ], + AccessControl: [ + 'hasRole(bytes32,address)', + 'getRoleAdmin(bytes32)', + 'grantRole(bytes32,address)', + 'revokeRole(bytes32,address)', + 'renounceRole(bytes32,address)', + ], + AccessControlEnumerable: [ + 'getRoleMember(bytes32,uint256)', + 'getRoleMemberCount(bytes32)', + ], + Governor: [ + 'name()', + 'version()', + 'COUNTING_MODE()', + 'hashProposal(address[],uint256[],bytes[],bytes32)', + 'state(uint256)', + 'proposalSnapshot(uint256)', + 'proposalDeadline(uint256)', + 'votingDelay()', + 'votingPeriod()', + 'quorum(uint256)', + 'getVotes(address,uint256)', + 'hasVoted(uint256,address)', + 'propose(address[],uint256[],bytes[],string)', + 'execute(address[],uint256[],bytes[],bytes32)', + 'castVote(uint256,uint8)', + 'castVoteWithReason(uint256,uint8,string)', + 'castVoteBySig(uint256,uint8,uint8,bytes32,bytes32)', + ], + GovernorWithParams: [ + 'name()', + 'version()', + 'COUNTING_MODE()', + 'hashProposal(address[],uint256[],bytes[],bytes32)', + 'state(uint256)', + 'proposalSnapshot(uint256)', + 'proposalDeadline(uint256)', + 'votingDelay()', + 'votingPeriod()', + 'quorum(uint256)', + 'getVotes(address,uint256)', + 'getVotesWithParams(address,uint256,bytes)', + 'hasVoted(uint256,address)', + 'propose(address[],uint256[],bytes[],string)', + 'execute(address[],uint256[],bytes[],bytes32)', + 'castVote(uint256,uint8)', + 'castVoteWithReason(uint256,uint8,string)', + 'castVoteWithReasonAndParams(uint256,uint8,string,bytes)', + 'castVoteBySig(uint256,uint8,uint8,bytes32,bytes32)', + 'castVoteWithReasonAndParamsBySig(uint256,uint8,string,bytes,uint8,bytes32,bytes32)', + ], + GovernorTimelock: [ + 'timelock()', + 'proposalEta(uint256)', + 'queue(address[],uint256[],bytes[],bytes32)', + ], + ERC2981: [ + 'royaltyInfo(uint256,uint256)', + ], +}; + +const INTERFACE_IDS = {}; +const FN_SIGNATURES = {}; +for (const k of Object.getOwnPropertyNames(INTERFACES)) { + INTERFACE_IDS[k] = makeInterfaceId.ERC165(INTERFACES[k]); + for (const fnName of INTERFACES[k]) { + // the interface id of a single function is equivalent to its function signature + FN_SIGNATURES[fnName] = makeInterfaceId.ERC165([fnName]); + } +} + +function shouldSupportInterfaces (interfaces = []) { + describe('ERC165', function () { + beforeEach(function () { + this.contractUnderTest = this.mock || this.token || this.holder || this.accessControl; + }); + + it('supportsInterface uses less than 30k gas', async function () { + for (const k of interfaces) { + const interfaceId = INTERFACE_IDS[k]; + expect(await this.contractUnderTest.supportsInterface.estimateGas(interfaceId)).to.be.lte(30000); + } + }); + + it('all interfaces are reported as supported', async function () { + for (const k of interfaces) { + const interfaceId = INTERFACE_IDS[k]; + expect(await this.contractUnderTest.supportsInterface(interfaceId)).to.equal(true); + } + }); + + it('all interface functions are in ABI', async function () { + for (const k of interfaces) { + for (const fnName of INTERFACES[k]) { + const fnSig = FN_SIGNATURES[fnName]; + expect(this.contractUnderTest.abi.filter(fn => fn.signature === fnSig).length).to.equal(1); + } + } + }); + }); +} + +module.exports = { + shouldSupportInterfaces, +}; diff --git a/lib/openzeppelin-contracts/test/utils/math/Math.t.sol b/lib/openzeppelin-contracts/test/utils/math/Math.t.sol new file mode 100644 index 0000000..5542baf --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/math/Math.t.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +import "../../../contracts/utils/math/Math.sol"; +import "../../../contracts/utils/math/SafeMath.sol"; + +contract MathTest is Test { + // CEILDIV + function testCeilDiv(uint256 a, uint256 b) public { + vm.assume(b > 0); + + uint256 result = Math.ceilDiv(a, b); + + if (result == 0) { + assertEq(a, 0); + } else { + uint256 maxdiv = UINT256_MAX / b; + bool overflow = maxdiv * b < a; + assertTrue(a > b * (result - 1)); + assertTrue(overflow ? result == maxdiv + 1 : a <= b * result); + } + } + + // SQRT + function testSqrt(uint256 input, uint8 r) public { + Math.Rounding rounding = _asRounding(r); + + uint256 result = Math.sqrt(input, rounding); + + // square of result is bigger than input + if (_squareBigger(result, input)) { + assertTrue(rounding == Math.Rounding.Up); + assertTrue(_squareSmaller(result - 1, input)); + } + // square of result is smaller than input + else if (_squareSmaller(result, input)) { + assertFalse(rounding == Math.Rounding.Up); + assertTrue(_squareBigger(result + 1, input)); + } + // input is perfect square + else { + assertEq(result * result, input); + } + } + + function _squareBigger(uint256 value, uint256 ref) private pure returns (bool) { + (bool noOverflow, uint256 square) = SafeMath.tryMul(value, value); + return !noOverflow || square > ref; + } + + function _squareSmaller(uint256 value, uint256 ref) private pure returns (bool) { + return value * value < ref; + } + + // LOG2 + function testLog2(uint256 input, uint8 r) public { + Math.Rounding rounding = _asRounding(r); + + uint256 result = Math.log2(input, rounding); + + if (input == 0) { + assertEq(result, 0); + } else if (_powerOf2Bigger(result, input)) { + assertTrue(rounding == Math.Rounding.Up); + assertTrue(_powerOf2Smaller(result - 1, input)); + } else if (_powerOf2Smaller(result, input)) { + assertFalse(rounding == Math.Rounding.Up); + assertTrue(_powerOf2Bigger(result + 1, input)); + } else { + assertEq(2 ** result, input); + } + } + + function _powerOf2Bigger(uint256 value, uint256 ref) private pure returns (bool) { + return value >= 256 || 2 ** value > ref; // 2**256 overflows uint256 + } + + function _powerOf2Smaller(uint256 value, uint256 ref) private pure returns (bool) { + return 2 ** value < ref; + } + + // LOG10 + function testLog10(uint256 input, uint8 r) public { + Math.Rounding rounding = _asRounding(r); + + uint256 result = Math.log10(input, rounding); + + if (input == 0) { + assertEq(result, 0); + } else if (_powerOf10Bigger(result, input)) { + assertTrue(rounding == Math.Rounding.Up); + assertTrue(_powerOf10Smaller(result - 1, input)); + } else if (_powerOf10Smaller(result, input)) { + assertFalse(rounding == Math.Rounding.Up); + assertTrue(_powerOf10Bigger(result + 1, input)); + } else { + assertEq(10 ** result, input); + } + } + + function _powerOf10Bigger(uint256 value, uint256 ref) private pure returns (bool) { + return value >= 78 || 10 ** value > ref; // 10**78 overflows uint256 + } + + function _powerOf10Smaller(uint256 value, uint256 ref) private pure returns (bool) { + return 10 ** value < ref; + } + + // LOG256 + function testLog256(uint256 input, uint8 r) public { + Math.Rounding rounding = _asRounding(r); + + uint256 result = Math.log256(input, rounding); + + if (input == 0) { + assertEq(result, 0); + } else if (_powerOf256Bigger(result, input)) { + assertTrue(rounding == Math.Rounding.Up); + assertTrue(_powerOf256Smaller(result - 1, input)); + } else if (_powerOf256Smaller(result, input)) { + assertFalse(rounding == Math.Rounding.Up); + assertTrue(_powerOf256Bigger(result + 1, input)); + } else { + assertEq(256 ** result, input); + } + } + + function _powerOf256Bigger(uint256 value, uint256 ref) private pure returns (bool) { + return value >= 32 || 256 ** value > ref; // 256**32 overflows uint256 + } + + function _powerOf256Smaller(uint256 value, uint256 ref) private pure returns (bool) { + return 256 ** value < ref; + } + + // MULDIV + function testMulDiv(uint256 x, uint256 y, uint256 d) public { + // Full precision for x * y + (uint256 xyHi, uint256 xyLo) = _mulHighLow(x, y); + + // Assume result won't overflow (see {testMulDivDomain}) + // This also checks that `d` is positive + vm.assume(xyHi < d); + + // Perform muldiv + uint256 q = Math.mulDiv(x, y, d); + + // Full precision for q * d + (uint256 qdHi, uint256 qdLo) = _mulHighLow(q, d); + // Add remainder of x * y / d (computed as rem = (x * y % d)) + (uint256 qdRemLo, uint256 c) = _addCarry(qdLo, _mulmod(x, y, d)); + uint256 qdRemHi = qdHi + c; + + // Full precision check that x * y = q * d + rem + assertEq(xyHi, qdRemHi); + assertEq(xyLo, qdRemLo); + } + + function testMulDivDomain(uint256 x, uint256 y, uint256 d) public { + (uint256 xyHi, ) = _mulHighLow(x, y); + + // Violate {testMulDiv} assumption (covers d is 0 and result overflow) + vm.assume(xyHi >= d); + + // we are outside the scope of {testMulDiv}, we expect muldiv to revert + try this.muldiv(x, y, d) returns (uint256) { + fail(); + } catch {} + } + + // External call + function muldiv(uint256 x, uint256 y, uint256 d) external pure returns (uint256) { + return Math.mulDiv(x, y, d); + } + + // Helpers + function _asRounding(uint8 r) private returns (Math.Rounding) { + vm.assume(r < uint8(type(Math.Rounding).max)); + return Math.Rounding(r); + } + + function _mulmod(uint256 x, uint256 y, uint256 z) private pure returns (uint256 r) { + assembly { + r := mulmod(x, y, z) + } + } + + function _mulHighLow(uint256 x, uint256 y) private pure returns (uint256 high, uint256 low) { + (uint256 x0, uint256 x1) = (x & type(uint128).max, x >> 128); + (uint256 y0, uint256 y1) = (y & type(uint128).max, y >> 128); + + // Karatsuba algorithm + // https://en.wikipedia.org/wiki/Karatsuba_algorithm + uint256 z2 = x1 * y1; + uint256 z1a = x1 * y0; + uint256 z1b = x0 * y1; + uint256 z0 = x0 * y0; + + uint256 carry = ((z1a & type(uint128).max) + (z1b & type(uint128).max) + (z0 >> 128)) >> 128; + + high = z2 + (z1a >> 128) + (z1b >> 128) + carry; + + unchecked { + low = x * y; + } + } + + function _addCarry(uint256 x, uint256 y) private pure returns (uint256 res, uint256 carry) { + unchecked { + res = x + y; + } + carry = res < x ? 1 : 0; + } +} diff --git a/lib/openzeppelin-contracts/test/utils/math/Math.test.js b/lib/openzeppelin-contracts/test/utils/math/Math.test.js new file mode 100644 index 0000000..2b5448b --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/math/Math.test.js @@ -0,0 +1,312 @@ +const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { MAX_UINT256 } = constants; +const { Rounding } = require('../../helpers/enums.js'); + +const MathMock = artifacts.require('MathMock'); + +contract('Math', function (accounts) { + const min = new BN('1234'); + const max = new BN('5678'); + const MAX_UINT256_SUB1 = MAX_UINT256.sub(new BN('1')); + const MAX_UINT256_SUB2 = MAX_UINT256.sub(new BN('2')); + + beforeEach(async function () { + this.math = await MathMock.new(); + }); + + describe('max', function () { + it('is correctly detected in first argument position', async function () { + expect(await this.math.max(max, min)).to.be.bignumber.equal(max); + }); + + it('is correctly detected in second argument position', async function () { + expect(await this.math.max(min, max)).to.be.bignumber.equal(max); + }); + }); + + describe('min', function () { + it('is correctly detected in first argument position', async function () { + expect(await this.math.min(min, max)).to.be.bignumber.equal(min); + }); + + it('is correctly detected in second argument position', async function () { + expect(await this.math.min(max, min)).to.be.bignumber.equal(min); + }); + }); + + describe('average', function () { + function bnAverage (a, b) { + return a.add(b).divn(2); + } + + it('is correctly calculated with two odd numbers', async function () { + const a = new BN('57417'); + const b = new BN('95431'); + expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b)); + }); + + it('is correctly calculated with two even numbers', async function () { + const a = new BN('42304'); + const b = new BN('84346'); + expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b)); + }); + + it('is correctly calculated with one even and one odd number', async function () { + const a = new BN('57417'); + const b = new BN('84346'); + expect(await this.math.average(a, b)).to.be.bignumber.equal(bnAverage(a, b)); + }); + + it('is correctly calculated with two max uint256 numbers', async function () { + const a = MAX_UINT256; + expect(await this.math.average(a, a)).to.be.bignumber.equal(bnAverage(a, a)); + }); + }); + + describe('ceilDiv', function () { + it('does not round up on exact division', async function () { + const a = new BN('10'); + const b = new BN('5'); + expect(await this.math.ceilDiv(a, b)).to.be.bignumber.equal('2'); + }); + + it('rounds up on division with remainders', async function () { + const a = new BN('42'); + const b = new BN('13'); + expect(await this.math.ceilDiv(a, b)).to.be.bignumber.equal('4'); + }); + + it('does not overflow', async function () { + const b = new BN('2'); + const result = new BN('1').shln(255); + expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(result); + }); + + it('correctly computes max uint256 divided by 1', async function () { + const b = new BN('1'); + expect(await this.math.ceilDiv(MAX_UINT256, b)).to.be.bignumber.equal(MAX_UINT256); + }); + }); + + describe('muldiv', function () { + it('divide by 0', async function () { + await expectRevert.unspecified(this.math.mulDiv(1, 1, 0, Rounding.Down)); + }); + + describe('does round down', async function () { + it('small values', async function () { + expect(await this.math.mulDiv('3', '4', '5', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.mulDiv('3', '5', '5', Rounding.Down)).to.be.bignumber.equal('3'); + }); + + it('large values', async function () { + expect(await this.math.mulDiv( + new BN('42'), + MAX_UINT256_SUB1, + MAX_UINT256, + Rounding.Down, + )).to.be.bignumber.equal(new BN('41')); + + expect(await this.math.mulDiv( + new BN('17'), + MAX_UINT256, + MAX_UINT256, + Rounding.Down, + )).to.be.bignumber.equal(new BN('17')); + + expect(await this.math.mulDiv( + MAX_UINT256_SUB1, + MAX_UINT256_SUB1, + MAX_UINT256, + Rounding.Down, + )).to.be.bignumber.equal(MAX_UINT256_SUB2); + + expect(await this.math.mulDiv( + MAX_UINT256, + MAX_UINT256_SUB1, + MAX_UINT256, + Rounding.Down, + )).to.be.bignumber.equal(MAX_UINT256_SUB1); + + expect(await this.math.mulDiv( + MAX_UINT256, + MAX_UINT256, + MAX_UINT256, + Rounding.Down, + )).to.be.bignumber.equal(MAX_UINT256); + }); + }); + + describe('does round up', async function () { + it('small values', async function () { + expect(await this.math.mulDiv('3', '4', '5', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.mulDiv('3', '5', '5', Rounding.Up)).to.be.bignumber.equal('3'); + }); + + it('large values', async function () { + expect(await this.math.mulDiv( + new BN('42'), + MAX_UINT256_SUB1, + MAX_UINT256, + Rounding.Up, + )).to.be.bignumber.equal(new BN('42')); + + expect(await this.math.mulDiv( + new BN('17'), + MAX_UINT256, + MAX_UINT256, + Rounding.Up, + )).to.be.bignumber.equal(new BN('17')); + + expect(await this.math.mulDiv( + MAX_UINT256_SUB1, + MAX_UINT256_SUB1, + MAX_UINT256, + Rounding.Up, + )).to.be.bignumber.equal(MAX_UINT256_SUB1); + + expect(await this.math.mulDiv( + MAX_UINT256, + MAX_UINT256_SUB1, + MAX_UINT256, + Rounding.Up, + )).to.be.bignumber.equal(MAX_UINT256_SUB1); + + expect(await this.math.mulDiv( + MAX_UINT256, + MAX_UINT256, + MAX_UINT256, + Rounding.Up, + )).to.be.bignumber.equal(MAX_UINT256); + }); + }); + }); + + describe('sqrt', function () { + it('rounds down', async function () { + expect(await this.math.sqrt('0', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.sqrt('1', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.sqrt('2', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.sqrt('3', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.sqrt('4', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.sqrt('144', Rounding.Down)).to.be.bignumber.equal('12'); + expect(await this.math.sqrt('999999', Rounding.Down)).to.be.bignumber.equal('999'); + expect(await this.math.sqrt('1000000', Rounding.Down)).to.be.bignumber.equal('1000'); + expect(await this.math.sqrt('1000001', Rounding.Down)).to.be.bignumber.equal('1000'); + expect(await this.math.sqrt('1002000', Rounding.Down)).to.be.bignumber.equal('1000'); + expect(await this.math.sqrt('1002001', Rounding.Down)).to.be.bignumber.equal('1001'); + expect(await this.math.sqrt(MAX_UINT256, Rounding.Down)) + .to.be.bignumber.equal('340282366920938463463374607431768211455'); + }); + + it('rounds up', async function () { + expect(await this.math.sqrt('0', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.sqrt('1', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.sqrt('2', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.sqrt('3', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.sqrt('4', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.sqrt('144', Rounding.Up)).to.be.bignumber.equal('12'); + expect(await this.math.sqrt('999999', Rounding.Up)).to.be.bignumber.equal('1000'); + expect(await this.math.sqrt('1000000', Rounding.Up)).to.be.bignumber.equal('1000'); + expect(await this.math.sqrt('1000001', Rounding.Up)).to.be.bignumber.equal('1001'); + expect(await this.math.sqrt('1002000', Rounding.Up)).to.be.bignumber.equal('1001'); + expect(await this.math.sqrt('1002001', Rounding.Up)).to.be.bignumber.equal('1001'); + expect(await this.math.sqrt(MAX_UINT256, Rounding.Up)) + .to.be.bignumber.equal('340282366920938463463374607431768211456'); + }); + }); + + describe('log', function () { + describe('log2', function () { + it('rounds down', async function () { + expect(await this.math.log2('0', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log2('1', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log2('2', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log2('3', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log2('4', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log2('5', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log2('6', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log2('7', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log2('8', Rounding.Down)).to.be.bignumber.equal('3'); + expect(await this.math.log2('9', Rounding.Down)).to.be.bignumber.equal('3'); + expect(await this.math.log2(MAX_UINT256, Rounding.Down)).to.be.bignumber.equal('255'); + }); + + it('rounds up', async function () { + expect(await this.math.log2('0', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.log2('1', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.log2('2', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log2('3', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log2('4', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log2('5', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log2('6', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log2('7', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log2('8', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log2(MAX_UINT256, Rounding.Up)).to.be.bignumber.equal('256'); + }); + }); + + describe('log10', function () { + it('rounds down', async function () { + expect(await this.math.log10('0', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log10('1', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log10('2', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log10('9', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log10('10', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log10('11', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log10('99', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log10('100', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log10('101', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log10('999', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log10('1000', Rounding.Down)).to.be.bignumber.equal('3'); + expect(await this.math.log10('1001', Rounding.Down)).to.be.bignumber.equal('3'); + expect(await this.math.log10(MAX_UINT256, Rounding.Down)).to.be.bignumber.equal('77'); + }); + + it('rounds up', async function () { + expect(await this.math.log10('0', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.log10('1', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.log10('2', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log10('9', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log10('10', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log10('11', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log10('99', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log10('100', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log10('101', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log10('999', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log10('1000', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log10('1001', Rounding.Up)).to.be.bignumber.equal('4'); + expect(await this.math.log10(MAX_UINT256, Rounding.Up)).to.be.bignumber.equal('78'); + }); + }); + + describe('log256', function () { + it('rounds down', async function () { + expect(await this.math.log256('0', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log256('1', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log256('2', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log256('255', Rounding.Down)).to.be.bignumber.equal('0'); + expect(await this.math.log256('256', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log256('257', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log256('65535', Rounding.Down)).to.be.bignumber.equal('1'); + expect(await this.math.log256('65536', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log256('65537', Rounding.Down)).to.be.bignumber.equal('2'); + expect(await this.math.log256(MAX_UINT256, Rounding.Down)).to.be.bignumber.equal('31'); + }); + + it('rounds up', async function () { + expect(await this.math.log256('0', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.log256('1', Rounding.Up)).to.be.bignumber.equal('0'); + expect(await this.math.log256('2', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log256('255', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log256('256', Rounding.Up)).to.be.bignumber.equal('1'); + expect(await this.math.log256('257', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log256('65535', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log256('65536', Rounding.Up)).to.be.bignumber.equal('2'); + expect(await this.math.log256('65537', Rounding.Up)).to.be.bignumber.equal('3'); + expect(await this.math.log256(MAX_UINT256, Rounding.Up)).to.be.bignumber.equal('32'); + }); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/math/SafeCast.test.js b/lib/openzeppelin-contracts/test/utils/math/SafeCast.test.js new file mode 100644 index 0000000..97fc22e --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/math/SafeCast.test.js @@ -0,0 +1,164 @@ +const { BN, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { range } = require('../../../scripts/helpers'); + +const SafeCastMock = artifacts.require('SafeCastMock'); + +contract('SafeCast', async (accounts) => { + beforeEach(async function () { + this.safeCast = await SafeCastMock.new(); + }); + + function testToUint (bits) { + describe(`toUint${bits}`, () => { + const maxValue = new BN('2').pow(new BN(bits)).subn(1); + + it('downcasts 0', async function () { + expect(await this.safeCast[`toUint${bits}`](0)).to.be.bignumber.equal('0'); + }); + + it('downcasts 1', async function () { + expect(await this.safeCast[`toUint${bits}`](1)).to.be.bignumber.equal('1'); + }); + + it(`downcasts 2^${bits} - 1 (${maxValue})`, async function () { + expect(await this.safeCast[`toUint${bits}`](maxValue)).to.be.bignumber.equal(maxValue); + }); + + it(`reverts when downcasting 2^${bits} (${maxValue.addn(1)})`, async function () { + await expectRevert( + this.safeCast[`toUint${bits}`](maxValue.addn(1)), + `SafeCast: value doesn't fit in ${bits} bits`, + ); + }); + + it(`reverts when downcasting 2^${bits} + 1 (${maxValue.addn(2)})`, async function () { + await expectRevert( + this.safeCast[`toUint${bits}`](maxValue.addn(2)), + `SafeCast: value doesn't fit in ${bits} bits`, + ); + }); + }); + } + + range(8, 256, 8).forEach(bits => testToUint(bits)); + + describe('toUint256', () => { + const maxInt256 = new BN('2').pow(new BN(255)).subn(1); + const minInt256 = new BN('2').pow(new BN(255)).neg(); + + it('casts 0', async function () { + expect(await this.safeCast.toUint256(0)).to.be.bignumber.equal('0'); + }); + + it('casts 1', async function () { + expect(await this.safeCast.toUint256(1)).to.be.bignumber.equal('1'); + }); + + it(`casts INT256_MAX (${maxInt256})`, async function () { + expect(await this.safeCast.toUint256(maxInt256)).to.be.bignumber.equal(maxInt256); + }); + + it('reverts when casting -1', async function () { + await expectRevert( + this.safeCast.toUint256(-1), + 'SafeCast: value must be positive', + ); + }); + + it(`reverts when casting INT256_MIN (${minInt256})`, async function () { + await expectRevert( + this.safeCast.toUint256(minInt256), + 'SafeCast: value must be positive', + ); + }); + }); + + function testToInt (bits) { + describe(`toInt${bits}`, () => { + const minValue = new BN('-2').pow(new BN(bits - 1)); + const maxValue = new BN('2').pow(new BN(bits - 1)).subn(1); + + it('downcasts 0', async function () { + expect(await this.safeCast[`toInt${bits}`](0)).to.be.bignumber.equal('0'); + }); + + it('downcasts 1', async function () { + expect(await this.safeCast[`toInt${bits}`](1)).to.be.bignumber.equal('1'); + }); + + it('downcasts -1', async function () { + expect(await this.safeCast[`toInt${bits}`](-1)).to.be.bignumber.equal('-1'); + }); + + it(`downcasts -2^${bits - 1} (${minValue})`, async function () { + expect(await this.safeCast[`toInt${bits}`](minValue)).to.be.bignumber.equal(minValue); + }); + + it(`downcasts 2^${bits - 1} - 1 (${maxValue})`, async function () { + expect(await this.safeCast[`toInt${bits}`](maxValue)).to.be.bignumber.equal(maxValue); + }); + + it(`reverts when downcasting -2^${bits - 1} - 1 (${minValue.subn(1)})`, async function () { + await expectRevert( + this.safeCast[`toInt${bits}`](minValue.subn(1)), + `SafeCast: value doesn't fit in ${bits} bits`, + ); + }); + + it(`reverts when downcasting -2^${bits - 1} - 2 (${minValue.subn(2)})`, async function () { + await expectRevert( + this.safeCast[`toInt${bits}`](minValue.subn(2)), + `SafeCast: value doesn't fit in ${bits} bits`, + ); + }); + + it(`reverts when downcasting 2^${bits - 1} (${maxValue.addn(1)})`, async function () { + await expectRevert( + this.safeCast[`toInt${bits}`](maxValue.addn(1)), + `SafeCast: value doesn't fit in ${bits} bits`, + ); + }); + + it(`reverts when downcasting 2^${bits - 1} + 1 (${maxValue.addn(2)})`, async function () { + await expectRevert( + this.safeCast[`toInt${bits}`](maxValue.addn(2)), + `SafeCast: value doesn't fit in ${bits} bits`, + ); + }); + }); + } + + range(8, 256, 8).forEach(bits => testToInt(bits)); + + describe('toInt256', () => { + const maxUint256 = new BN('2').pow(new BN(256)).subn(1); + const maxInt256 = new BN('2').pow(new BN(255)).subn(1); + + it('casts 0', async function () { + expect(await this.safeCast.toInt256(0)).to.be.bignumber.equal('0'); + }); + + it('casts 1', async function () { + expect(await this.safeCast.toInt256(1)).to.be.bignumber.equal('1'); + }); + + it(`casts INT256_MAX (${maxInt256})`, async function () { + expect(await this.safeCast.toInt256(maxInt256)).to.be.bignumber.equal(maxInt256); + }); + + it(`reverts when casting INT256_MAX + 1 (${maxInt256.addn(1)})`, async function () { + await expectRevert( + this.safeCast.toInt256(maxInt256.addn(1)), + 'SafeCast: value doesn\'t fit in an int256', + ); + }); + + it(`reverts when casting UINT256_MAX (${maxUint256})`, async function () { + await expectRevert( + this.safeCast.toInt256(maxUint256), + 'SafeCast: value doesn\'t fit in an int256', + ); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/math/SafeMath.test.js b/lib/openzeppelin-contracts/test/utils/math/SafeMath.test.js new file mode 100644 index 0000000..7c9b937 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/math/SafeMath.test.js @@ -0,0 +1,403 @@ +const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); +const { MAX_UINT256 } = constants; + +const { expect } = require('chai'); + +const SafeMathMock = artifacts.require('SafeMathMock'); + +function expectStruct (value, expected) { + for (const key in expected) { + if (BN.isBN(value[key])) { + expect(value[key]).to.be.bignumber.equal(expected[key]); + } else { + expect(value[key]).to.be.equal(expected[key]); + } + } +} + +contract('SafeMath', function (accounts) { + beforeEach(async function () { + this.safeMath = await SafeMathMock.new(); + }); + + async function testCommutative (fn, lhs, rhs, expected, ...extra) { + expect(await fn(lhs, rhs, ...extra)).to.be.bignumber.equal(expected); + expect(await fn(rhs, lhs, ...extra)).to.be.bignumber.equal(expected); + } + + async function testFailsCommutative (fn, lhs, rhs, reason, ...extra) { + if (reason === undefined) { + await expectRevert.unspecified(fn(lhs, rhs, ...extra)); + await expectRevert.unspecified(fn(rhs, lhs, ...extra)); + } else { + await expectRevert(fn(lhs, rhs, ...extra), reason); + await expectRevert(fn(rhs, lhs, ...extra), reason); + } + } + + async function testCommutativeIterable (fn, lhs, rhs, expected, ...extra) { + expectStruct(await fn(lhs, rhs, ...extra), expected); + expectStruct(await fn(rhs, lhs, ...extra), expected); + } + + describe('with flag', function () { + describe('add', function () { + it('adds correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + testCommutativeIterable(this.safeMath.tryAdd, a, b, { flag: true, value: a.add(b) }); + }); + + it('reverts on addition overflow', async function () { + const a = MAX_UINT256; + const b = new BN('1'); + + testCommutativeIterable(this.safeMath.tryAdd, a, b, { flag: false, value: '0' }); + }); + }); + + describe('sub', function () { + it('subtracts correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + expectStruct(await this.safeMath.trySub(a, b), { flag: true, value: a.sub(b) }); + }); + + it('reverts if subtraction result would be negative', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.trySub(a, b), { flag: false, value: '0' }); + }); + }); + + describe('mul', function () { + it('multiplies correctly', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + testCommutativeIterable(this.safeMath.tryMul, a, b, { flag: true, value: a.mul(b) }); + }); + + it('multiplies by zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + testCommutativeIterable(this.safeMath.tryMul, a, b, { flag: true, value: a.mul(b) }); + }); + + it('reverts on multiplication overflow', async function () { + const a = MAX_UINT256; + const b = new BN('2'); + + testCommutativeIterable(this.safeMath.tryMul, a, b, { flag: false, value: '0' }); + }); + }); + + describe('div', function () { + it('divides correctly', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryDiv(a, b), { flag: true, value: a.div(b) }); + }); + + it('divides zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryDiv(a, b), { flag: true, value: a.div(b) }); + }); + + it('returns complete number result on non-even division', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryDiv(a, b), { flag: true, value: a.div(b) }); + }); + + it('reverts on division by zero', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + expectStruct(await this.safeMath.tryDiv(a, b), { flag: false, value: '0' }); + }); + }); + + describe('mod', function () { + describe('modulos correctly', async function () { + it('when the dividend is smaller than the divisor', async function () { + const a = new BN('284'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) }); + }); + + it('when the dividend is equal to the divisor', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) }); + }); + + it('when the dividend is larger than the divisor', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) }); + }); + + it('when the dividend is a multiple of the divisor', async function () { + const a = new BN('17034'); // 17034 == 5678 * 3 + const b = new BN('5678'); + + expectStruct(await this.safeMath.tryMod(a, b), { flag: true, value: a.mod(b) }); + }); + }); + + it('reverts with a 0 divisor', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + expectStruct(await this.safeMath.tryMod(a, b), { flag: false, value: '0' }); + }); + }); + }); + + describe('with default revert message', function () { + describe('add', function () { + it('adds correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + await testCommutative(this.safeMath.doAdd, a, b, a.add(b)); + }); + + it('reverts on addition overflow', async function () { + const a = MAX_UINT256; + const b = new BN('1'); + + await testFailsCommutative(this.safeMath.doAdd, a, b, undefined); + }); + }); + + describe('sub', function () { + it('subtracts correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + expect(await this.safeMath.doSub(a, b)).to.be.bignumber.equal(a.sub(b)); + }); + + it('reverts if subtraction result would be negative', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + await expectRevert.unspecified(this.safeMath.doSub(a, b)); + }); + }); + + describe('mul', function () { + it('multiplies correctly', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + await testCommutative(this.safeMath.doMul, a, b, a.mul(b)); + }); + + it('multiplies by zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + await testCommutative(this.safeMath.doMul, a, b, '0'); + }); + + it('reverts on multiplication overflow', async function () { + const a = MAX_UINT256; + const b = new BN('2'); + + await testFailsCommutative(this.safeMath.doMul, a, b, undefined); + }); + }); + + describe('div', function () { + it('divides correctly', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expect(await this.safeMath.doDiv(a, b)).to.be.bignumber.equal(a.div(b)); + }); + + it('divides zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + expect(await this.safeMath.doDiv(a, b)).to.be.bignumber.equal('0'); + }); + + it('returns complete number result on non-even division', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expect(await this.safeMath.doDiv(a, b)).to.be.bignumber.equal('1'); + }); + + it('reverts on division by zero', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + await expectRevert.unspecified(this.safeMath.doDiv(a, b)); + }); + }); + + describe('mod', function () { + describe('modulos correctly', async function () { + it('when the dividend is smaller than the divisor', async function () { + const a = new BN('284'); + const b = new BN('5678'); + + expect(await this.safeMath.doMod(a, b)).to.be.bignumber.equal(a.mod(b)); + }); + + it('when the dividend is equal to the divisor', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expect(await this.safeMath.doMod(a, b)).to.be.bignumber.equal(a.mod(b)); + }); + + it('when the dividend is larger than the divisor', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expect(await this.safeMath.doMod(a, b)).to.be.bignumber.equal(a.mod(b)); + }); + + it('when the dividend is a multiple of the divisor', async function () { + const a = new BN('17034'); // 17034 == 5678 * 3 + const b = new BN('5678'); + + expect(await this.safeMath.doMod(a, b)).to.be.bignumber.equal(a.mod(b)); + }); + }); + + it('reverts with a 0 divisor', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + await expectRevert.unspecified(this.safeMath.doMod(a, b)); + }); + }); + }); + + describe('with custom revert message', function () { + describe('sub', function () { + it('subtracts correctly', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + expect(await this.safeMath.subWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.sub(b)); + }); + + it('reverts if subtraction result would be negative', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + await expectRevert(this.safeMath.subWithMessage(a, b, 'MyErrorMessage'), 'MyErrorMessage'); + }); + }); + + describe('div', function () { + it('divides correctly', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expect(await this.safeMath.divWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.div(b)); + }); + + it('divides zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + expect(await this.safeMath.divWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal('0'); + }); + + it('returns complete number result on non-even division', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expect(await this.safeMath.divWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal('1'); + }); + + it('reverts on division by zero', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + await expectRevert(this.safeMath.divWithMessage(a, b, 'MyErrorMessage'), 'MyErrorMessage'); + }); + }); + + describe('mod', function () { + describe('modulos correctly', async function () { + it('when the dividend is smaller than the divisor', async function () { + const a = new BN('284'); + const b = new BN('5678'); + + expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b)); + }); + + it('when the dividend is equal to the divisor', async function () { + const a = new BN('5678'); + const b = new BN('5678'); + + expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b)); + }); + + it('when the dividend is larger than the divisor', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b)); + }); + + it('when the dividend is a multiple of the divisor', async function () { + const a = new BN('17034'); // 17034 == 5678 * 3 + const b = new BN('5678'); + + expect(await this.safeMath.modWithMessage(a, b, 'MyErrorMessage')).to.be.bignumber.equal(a.mod(b)); + }); + }); + + it('reverts with a 0 divisor', async function () { + const a = new BN('5678'); + const b = new BN('0'); + + await expectRevert(this.safeMath.modWithMessage(a, b, 'MyErrorMessage'), 'MyErrorMessage'); + }); + }); + }); + + describe('memory leakage', function () { + it('add', async function () { + expect(await this.safeMath.addMemoryCheck()).to.be.bignumber.equal('0'); + }); + + it('sub', async function () { + expect(await this.safeMath.subMemoryCheck()).to.be.bignumber.equal('0'); + }); + + it('mul', async function () { + expect(await this.safeMath.mulMemoryCheck()).to.be.bignumber.equal('0'); + }); + + it('div', async function () { + expect(await this.safeMath.divMemoryCheck()).to.be.bignumber.equal('0'); + }); + + it('mod', async function () { + expect(await this.safeMath.modMemoryCheck()).to.be.bignumber.equal('0'); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/math/SignedMath.test.js b/lib/openzeppelin-contracts/test/utils/math/SignedMath.test.js new file mode 100644 index 0000000..8e9826f --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/math/SignedMath.test.js @@ -0,0 +1,93 @@ +const { BN, constants } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); +const { MIN_INT256, MAX_INT256 } = constants; + +const SignedMathMock = artifacts.require('SignedMathMock'); + +contract('SignedMath', function (accounts) { + const min = new BN('-1234'); + const max = new BN('5678'); + + beforeEach(async function () { + this.math = await SignedMathMock.new(); + }); + + describe('max', function () { + it('is correctly detected in first argument position', async function () { + expect(await this.math.max(max, min)).to.be.bignumber.equal(max); + }); + + it('is correctly detected in second argument position', async function () { + expect(await this.math.max(min, max)).to.be.bignumber.equal(max); + }); + }); + + describe('min', function () { + it('is correctly detected in first argument position', async function () { + expect(await this.math.min(min, max)).to.be.bignumber.equal(min); + }); + + it('is correctly detected in second argument position', async function () { + expect(await this.math.min(max, min)).to.be.bignumber.equal(min); + }); + }); + + describe('average', function () { + function bnAverage (a, b) { + return a.add(b).divn(2); + } + + it('is correctly calculated with various input', async function () { + const valuesX = [ + new BN('0'), + new BN('3'), + new BN('-3'), + new BN('4'), + new BN('-4'), + new BN('57417'), + new BN('-57417'), + new BN('42304'), + new BN('-42304'), + MIN_INT256, + MAX_INT256, + ]; + + const valuesY = [ + new BN('0'), + new BN('5'), + new BN('-5'), + new BN('2'), + new BN('-2'), + new BN('57417'), + new BN('-57417'), + new BN('42304'), + new BN('-42304'), + MIN_INT256, + MAX_INT256, + ]; + + for (const x of valuesX) { + for (const y of valuesY) { + expect(await this.math.average(x, y)) + .to.be.bignumber.equal(bnAverage(x, y), `Bad result for average(${x}, ${y})`); + } + } + }); + }); + + describe('abs', function () { + for (const n of [ + MIN_INT256, + MIN_INT256.addn(1), + new BN('-1'), + new BN('0'), + new BN('1'), + MAX_INT256.subn(1), + MAX_INT256, + ]) { + it(`correctly computes the absolute value of ${n}`, async function () { + expect(await this.math.abs(n)).to.be.bignumber.equal(n.abs()); + }); + } + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/math/SignedSafeMath.test.js b/lib/openzeppelin-contracts/test/utils/math/SignedSafeMath.test.js new file mode 100644 index 0000000..c6aa15e --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/math/SignedSafeMath.test.js @@ -0,0 +1,152 @@ +const { BN, constants, expectRevert } = require('@openzeppelin/test-helpers'); +const { MAX_INT256, MIN_INT256 } = constants; + +const { expect } = require('chai'); + +const SignedSafeMathMock = artifacts.require('SignedSafeMathMock'); + +contract('SignedSafeMath', function (accounts) { + beforeEach(async function () { + this.safeMath = await SignedSafeMathMock.new(); + }); + + async function testCommutative (fn, lhs, rhs, expected) { + expect(await fn(lhs, rhs)).to.be.bignumber.equal(expected); + expect(await fn(rhs, lhs)).to.be.bignumber.equal(expected); + } + + async function testFailsCommutative (fn, lhs, rhs) { + await expectRevert.unspecified(fn(lhs, rhs)); + await expectRevert.unspecified(fn(rhs, lhs)); + } + + describe('add', function () { + it('adds correctly if it does not overflow and the result is positive', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + await testCommutative(this.safeMath.add, a, b, a.add(b)); + }); + + it('adds correctly if it does not overflow and the result is negative', async function () { + const a = MAX_INT256; + const b = MIN_INT256; + + await testCommutative(this.safeMath.add, a, b, a.add(b)); + }); + + it('reverts on positive addition overflow', async function () { + const a = MAX_INT256; + const b = new BN('1'); + + await testFailsCommutative(this.safeMath.add, a, b); + }); + + it('reverts on negative addition overflow', async function () { + const a = MIN_INT256; + const b = new BN('-1'); + + await testFailsCommutative(this.safeMath.add, a, b); + }); + }); + + describe('sub', function () { + it('subtracts correctly if it does not overflow and the result is positive', async function () { + const a = new BN('5678'); + const b = new BN('1234'); + + const result = await this.safeMath.sub(a, b); + expect(result).to.be.bignumber.equal(a.sub(b)); + }); + + it('subtracts correctly if it does not overflow and the result is negative', async function () { + const a = new BN('1234'); + const b = new BN('5678'); + + const result = await this.safeMath.sub(a, b); + expect(result).to.be.bignumber.equal(a.sub(b)); + }); + + it('reverts on positive subtraction overflow', async function () { + const a = MAX_INT256; + const b = new BN('-1'); + + await expectRevert.unspecified(this.safeMath.sub(a, b)); + }); + + it('reverts on negative subtraction overflow', async function () { + const a = MIN_INT256; + const b = new BN('1'); + + await expectRevert.unspecified(this.safeMath.sub(a, b)); + }); + }); + + describe('mul', function () { + it('multiplies correctly', async function () { + const a = new BN('5678'); + const b = new BN('-1234'); + + await testCommutative(this.safeMath.mul, a, b, a.mul(b)); + }); + + it('multiplies by zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + await testCommutative(this.safeMath.mul, a, b, '0'); + }); + + it('reverts on multiplication overflow, positive operands', async function () { + const a = MAX_INT256; + const b = new BN('2'); + + await testFailsCommutative(this.safeMath.mul, a, b); + }); + + it('reverts when minimum integer is multiplied by -1', async function () { + const a = MIN_INT256; + const b = new BN('-1'); + + await testFailsCommutative(this.safeMath.mul, a, b); + }); + }); + + describe('div', function () { + it('divides correctly', async function () { + const a = new BN('-5678'); + const b = new BN('5678'); + + const result = await this.safeMath.div(a, b); + expect(result).to.be.bignumber.equal(a.div(b)); + }); + + it('divides zero correctly', async function () { + const a = new BN('0'); + const b = new BN('5678'); + + expect(await this.safeMath.div(a, b)).to.be.bignumber.equal('0'); + }); + + it('returns complete number result on non-even division', async function () { + const a = new BN('7000'); + const b = new BN('5678'); + + expect(await this.safeMath.div(a, b)).to.be.bignumber.equal('1'); + }); + + it('reverts on division by zero', async function () { + const a = new BN('-5678'); + const b = new BN('0'); + + await expectRevert.unspecified(this.safeMath.div(a, b)); + }); + + it('reverts on overflow, negative second', async function () { + const a = new BN(MIN_INT256); + const b = new BN('-1'); + + await expectRevert.unspecified(this.safeMath.div(a, b)); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/structs/BitMap.test.js b/lib/openzeppelin-contracts/test/utils/structs/BitMap.test.js new file mode 100644 index 0000000..58d70ca --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/structs/BitMap.test.js @@ -0,0 +1,145 @@ +const { BN } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const BitMap = artifacts.require('BitMapMock'); + +contract('BitMap', function (accounts) { + const keyA = new BN('7891'); + const keyB = new BN('451'); + const keyC = new BN('9592328'); + + beforeEach(async function () { + this.bitmap = await BitMap.new(); + }); + + it('starts empty', async function () { + expect(await this.bitmap.get(keyA)).to.equal(false); + expect(await this.bitmap.get(keyB)).to.equal(false); + expect(await this.bitmap.get(keyC)).to.equal(false); + }); + + describe('setTo', function () { + it('set a key to true', async function () { + await this.bitmap.setTo(keyA, true); + expect(await this.bitmap.get(keyA)).to.equal(true); + expect(await this.bitmap.get(keyB)).to.equal(false); + expect(await this.bitmap.get(keyC)).to.equal(false); + }); + + it('set a key to false', async function () { + await this.bitmap.setTo(keyA, true); + await this.bitmap.setTo(keyA, false); + expect(await this.bitmap.get(keyA)).to.equal(false); + expect(await this.bitmap.get(keyB)).to.equal(false); + expect(await this.bitmap.get(keyC)).to.equal(false); + }); + + it('set several consecutive keys', async function () { + await this.bitmap.setTo(keyA.addn(0), true); + await this.bitmap.setTo(keyA.addn(1), true); + await this.bitmap.setTo(keyA.addn(2), true); + await this.bitmap.setTo(keyA.addn(3), true); + await this.bitmap.setTo(keyA.addn(4), true); + await this.bitmap.setTo(keyA.addn(2), false); + await this.bitmap.setTo(keyA.addn(4), false); + expect(await this.bitmap.get(keyA.addn(0))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(1))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(2))).to.equal(false); + expect(await this.bitmap.get(keyA.addn(3))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(4))).to.equal(false); + }); + }); + + describe('set', function () { + it('adds a key', async function () { + await this.bitmap.set(keyA); + expect(await this.bitmap.get(keyA)).to.equal(true); + expect(await this.bitmap.get(keyB)).to.equal(false); + expect(await this.bitmap.get(keyC)).to.equal(false); + }); + + it('adds several keys', async function () { + await this.bitmap.set(keyA); + await this.bitmap.set(keyB); + expect(await this.bitmap.get(keyA)).to.equal(true); + expect(await this.bitmap.get(keyB)).to.equal(true); + expect(await this.bitmap.get(keyC)).to.equal(false); + }); + + it('adds several consecutive keys', async function () { + await this.bitmap.set(keyA.addn(0)); + await this.bitmap.set(keyA.addn(1)); + await this.bitmap.set(keyA.addn(3)); + expect(await this.bitmap.get(keyA.addn(0))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(1))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(2))).to.equal(false); + expect(await this.bitmap.get(keyA.addn(3))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(4))).to.equal(false); + }); + }); + + describe('unset', function () { + it('removes added keys', async function () { + await this.bitmap.set(keyA); + await this.bitmap.set(keyB); + await this.bitmap.unset(keyA); + expect(await this.bitmap.get(keyA)).to.equal(false); + expect(await this.bitmap.get(keyB)).to.equal(true); + expect(await this.bitmap.get(keyC)).to.equal(false); + }); + + it('removes consecutive added keys', async function () { + await this.bitmap.set(keyA.addn(0)); + await this.bitmap.set(keyA.addn(1)); + await this.bitmap.set(keyA.addn(3)); + await this.bitmap.unset(keyA.addn(1)); + expect(await this.bitmap.get(keyA.addn(0))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(1))).to.equal(false); + expect(await this.bitmap.get(keyA.addn(2))).to.equal(false); + expect(await this.bitmap.get(keyA.addn(3))).to.equal(true); + expect(await this.bitmap.get(keyA.addn(4))).to.equal(false); + }); + + it('adds and removes multiple keys', async function () { + // [] + + await this.bitmap.set(keyA); + await this.bitmap.set(keyC); + + // [A, C] + + await this.bitmap.unset(keyA); + await this.bitmap.unset(keyB); + + // [C] + + await this.bitmap.set(keyB); + + // [C, B] + + await this.bitmap.set(keyA); + await this.bitmap.unset(keyC); + + // [A, B] + + await this.bitmap.set(keyA); + await this.bitmap.set(keyB); + + // [A, B] + + await this.bitmap.set(keyC); + await this.bitmap.unset(keyA); + + // [B, C] + + await this.bitmap.set(keyA); + await this.bitmap.unset(keyB); + + // [A, C] + + expect(await this.bitmap.get(keyA)).to.equal(true); + expect(await this.bitmap.get(keyB)).to.equal(false); + expect(await this.bitmap.get(keyC)).to.equal(true); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/structs/DoubleEndedQueue.test.js b/lib/openzeppelin-contracts/test/utils/structs/DoubleEndedQueue.test.js new file mode 100644 index 0000000..545c82a --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/structs/DoubleEndedQueue.test.js @@ -0,0 +1,96 @@ +const { expectEvent } = require('@openzeppelin/test-helpers'); +const { expectRevertCustomError } = require('../../helpers/customError'); + +const Bytes32DequeMock = artifacts.require('Bytes32DequeMock'); + +/** Rebuild the content of the deque as a JS array. */ +async function getContent (deque) { + const length = await deque.length().then(bn => bn.toNumber()); + const values = await Promise.all(Array(length).fill().map((_, i) => deque.at(i))); + return values; +} + +contract('DoubleEndedQueue', function (accounts) { + const bytesA = '0xdeadbeef'.padEnd(66, '0'); + const bytesB = '0x0123456789'.padEnd(66, '0'); + const bytesC = '0x42424242'.padEnd(66, '0'); + const bytesD = '0x171717'.padEnd(66, '0'); + + beforeEach(async function () { + this.deque = await Bytes32DequeMock.new(); + }); + + describe('when empty', function () { + it('getters', async function () { + expect(await this.deque.empty()).to.be.equal(true); + expect(await getContent(this.deque)).to.have.ordered.members([]); + }); + + it('reverts on accesses', async function () { + await expectRevertCustomError(this.deque.popBack(), 'Empty()'); + await expectRevertCustomError(this.deque.popFront(), 'Empty()'); + await expectRevertCustomError(this.deque.back(), 'Empty()'); + await expectRevertCustomError(this.deque.front(), 'Empty()'); + }); + }); + + describe('when not empty', function () { + beforeEach(async function () { + await this.deque.pushBack(bytesB); + await this.deque.pushFront(bytesA); + await this.deque.pushBack(bytesC); + this.content = [ bytesA, bytesB, bytesC ]; + }); + + it('getters', async function () { + expect(await this.deque.empty()).to.be.equal(false); + expect(await this.deque.length()).to.be.bignumber.equal(this.content.length.toString()); + expect(await this.deque.front()).to.be.equal(this.content[0]); + expect(await this.deque.back()).to.be.equal(this.content[this.content.length - 1]); + expect(await getContent(this.deque)).to.have.ordered.members(this.content); + }); + + it('out of bounds access', async function () { + await expectRevertCustomError(this.deque.at(this.content.length), 'OutOfBounds()'); + }); + + describe('push', function () { + it('front', async function () { + await this.deque.pushFront(bytesD); + this.content.unshift(bytesD); // add element at the beginning + + expect(await getContent(this.deque)).to.have.ordered.members(this.content); + }); + + it('back', async function () { + await this.deque.pushBack(bytesD); + this.content.push(bytesD); // add element at the end + + expect(await getContent(this.deque)).to.have.ordered.members(this.content); + }); + }); + + describe('pop', function () { + it('front', async function () { + const value = this.content.shift(); // remove first element + expectEvent(await this.deque.popFront(), 'OperationResult', { value }); + + expect(await getContent(this.deque)).to.have.ordered.members(this.content); + }); + + it('back', async function () { + const value = this.content.pop(); // remove last element + expectEvent(await this.deque.popBack(), 'OperationResult', { value }); + + expect(await getContent(this.deque)).to.have.ordered.members(this.content); + }); + }); + + it('clear', async function () { + await this.deque.clear(); + + expect(await this.deque.empty()).to.be.equal(true); + expect(await getContent(this.deque)).to.have.ordered.members([]); + }); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.behavior.js b/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.behavior.js new file mode 100644 index 0000000..b1d0d0d --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.behavior.js @@ -0,0 +1,181 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +const zip = require('lodash.zip'); + +function shouldBehaveLikeMap (keys, values, zeroValue) { + const [keyA, keyB, keyC] = keys; + const [valueA, valueB, valueC] = values; + + async function expectMembersMatch (map, keys, values) { + expect(keys.length).to.equal(values.length); + + await Promise.all(keys.map(async key => + expect(await map.contains(key)).to.equal(true), + )); + + expect(await map.length()).to.bignumber.equal(keys.length.toString()); + + expect( + (await Promise.all(keys.map(key => map.get(key)))).map(k => k.toString()), + ).to.have.same.members( + values.map(value => value.toString()), + ); + + // To compare key-value pairs, we zip keys and values, and convert BNs to + // strings to workaround Chai limitations when dealing with nested arrays + expect(await Promise.all([...Array(keys.length).keys()].map(async (index) => { + const entry = await map.at(index); + return [entry.key.toString(), entry.value.toString()]; + }))).to.have.same.deep.members( + zip(keys.map(k => k.toString()), values.map(v => v.toString())), + ); + } + + it('starts empty', async function () { + expect(await this.map.contains(keyA)).to.equal(false); + + await expectMembersMatch(this.map, [], []); + }); + + describe('set', function () { + it('adds a key', async function () { + const receipt = await this.map.set(keyA, valueA); + expectEvent(receipt, 'OperationResult', { result: true }); + + await expectMembersMatch(this.map, [keyA], [valueA]); + }); + + it('adds several keys', async function () { + await this.map.set(keyA, valueA); + await this.map.set(keyB, valueB); + + await expectMembersMatch(this.map, [keyA, keyB], [valueA, valueB]); + expect(await this.map.contains(keyC)).to.equal(false); + }); + + it('returns false when adding keys already in the set', async function () { + await this.map.set(keyA, valueA); + + const receipt = (await this.map.set(keyA, valueA)); + expectEvent(receipt, 'OperationResult', { result: false }); + + await expectMembersMatch(this.map, [keyA], [valueA]); + }); + + it('updates values for keys already in the set', async function () { + await this.map.set(keyA, valueA); + + await this.map.set(keyA, valueB); + + await expectMembersMatch(this.map, [keyA], [valueB]); + }); + }); + + describe('remove', function () { + it('removes added keys', async function () { + await this.map.set(keyA, valueA); + + const receipt = await this.map.remove(keyA); + expectEvent(receipt, 'OperationResult', { result: true }); + + expect(await this.map.contains(keyA)).to.equal(false); + await expectMembersMatch(this.map, [], []); + }); + + it('returns false when removing keys not in the set', async function () { + const receipt = await this.map.remove(keyA); + expectEvent(receipt, 'OperationResult', { result: false }); + + expect(await this.map.contains(keyA)).to.equal(false); + }); + + it('adds and removes multiple keys', async function () { + // [] + + await this.map.set(keyA, valueA); + await this.map.set(keyC, valueC); + + // [A, C] + + await this.map.remove(keyA); + await this.map.remove(keyB); + + // [C] + + await this.map.set(keyB, valueB); + + // [C, B] + + await this.map.set(keyA, valueA); + await this.map.remove(keyC); + + // [A, B] + + await this.map.set(keyA, valueA); + await this.map.set(keyB, valueB); + + // [A, B] + + await this.map.set(keyC, valueC); + await this.map.remove(keyA); + + // [B, C] + + await this.map.set(keyA, valueA); + await this.map.remove(keyB); + + // [A, C] + + await expectMembersMatch(this.map, [keyA, keyC], [valueA, valueC]); + + expect(await this.map.contains(keyB)).to.equal(false); + }); + }); + + describe('read', function () { + beforeEach(async function () { + await this.map.set(keyA, valueA); + }); + + describe('get', function () { + it('existing value', async function () { + expect( + (await this.map.get(keyA)).toString(), + ).to.be.equal(valueA.toString()); + }); + it('missing value', async function () { + await expectRevert(this.map.get(keyB), 'EnumerableMap: nonexistent key'); + }); + }); + + describe('get with message', function () { + it('existing value', async function () { + expect( + (await this.map.getWithMessage(keyA, 'custom error string')) + .toString(), + ).to.be.equal(valueA.toString()); + }); + it('missing value', async function () { + await expectRevert(this.map.getWithMessage(keyB, 'custom error string'), 'custom error string'); + }); + }); + + describe('tryGet', function () { + it('existing value', async function () { + const result = await this.map.tryGet(keyA); + expect(result['0']).to.be.equal(true); + expect(result['1'].toString()).to.be.equal(valueA.toString()); + }); + it('missing value', async function () { + const result = await this.map.tryGet(keyB); + expect(result['0']).to.be.equal(false); + expect(result['1'].toString()).to.be.equal(zeroValue.toString()); + }); + }); + }); +} + +module.exports = { + shouldBehaveLikeMap, +}; diff --git a/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.test.js b/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.test.js new file mode 100644 index 0000000..58f4eb8 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/structs/EnumerableMap.test.js @@ -0,0 +1,86 @@ +const { BN, constants } = require('@openzeppelin/test-helpers'); + +const AddressToUintMapMock = artifacts.require('AddressToUintMapMock'); +const UintToAddressMapMock = artifacts.require('UintToAddressMapMock'); +const Bytes32ToBytes32MapMock = artifacts.require('Bytes32ToBytes32MapMock'); +const UintToUintMapMock = artifacts.require('UintToUintMapMock'); +const Bytes32ToUintMapMock = artifacts.require('Bytes32ToUintMapMock'); + +const { shouldBehaveLikeMap } = require('./EnumerableMap.behavior'); + +contract('EnumerableMap', function (accounts) { + const [ accountA, accountB, accountC ] = accounts; + + const keyA = new BN('7891'); + const keyB = new BN('451'); + const keyC = new BN('9592328'); + + const bytesA = '0xdeadbeef'.padEnd(66, '0'); + const bytesB = '0x0123456789'.padEnd(66, '0'); + const bytesC = '0x42424242'.padEnd(66, '0'); + + // AddressToUintMap + describe('AddressToUintMap', function () { + beforeEach(async function () { + this.map = await AddressToUintMapMock.new(); + }); + + shouldBehaveLikeMap( + [ accountA, accountB, accountC ], + [ keyA, keyB, keyC ], + new BN('0'), + ); + }); + + // UintToAddressMap + describe('UintToAddressMap', function () { + beforeEach(async function () { + this.map = await UintToAddressMapMock.new(); + }); + + shouldBehaveLikeMap( + [ keyA, keyB, keyC ], + [ accountA, accountB, accountC ], + constants.ZERO_ADDRESS, + ); + }); + + // Bytes32ToBytes32Map + describe('Bytes32ToBytes32Map', function () { + beforeEach(async function () { + this.map = await Bytes32ToBytes32MapMock.new(); + }); + + shouldBehaveLikeMap( + [ keyA, keyB, keyC ].map(k => '0x' + k.toString(16).padEnd(64, '0')), + [ bytesA, bytesB, bytesC ], + constants.ZERO_BYTES32, + ); + }); + + // UintToUintMap + describe('UintToUintMap', function () { + beforeEach(async function () { + this.map = await UintToUintMapMock.new(); + }); + + shouldBehaveLikeMap( + [ keyA, keyB, keyC ], + [ keyA, keyB, keyC ].map(k => k.add(new BN('1332'))), + new BN('0'), + ); + }); + + // Bytes32ToUintMap + describe('Bytes32ToUintMap', function () { + beforeEach(async function () { + this.map = await Bytes32ToUintMapMock.new(); + }); + + shouldBehaveLikeMap( + [ bytesA, bytesB, bytesC ], + [ keyA, keyB, keyC ], + new BN('0'), + ); + }); +}); diff --git a/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.behavior.js b/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.behavior.js new file mode 100644 index 0000000..17e0866 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.behavior.js @@ -0,0 +1,131 @@ +const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); +const { expect } = require('chai'); + +function shouldBehaveLikeSet (valueA, valueB, valueC) { + async function expectMembersMatch (set, values) { + const contains = await Promise.all(values.map(value => set.contains(value))); + expect(contains.every(Boolean)).to.be.equal(true); + + const length = await set.length(); + expect(length).to.bignumber.equal(values.length.toString()); + + // To compare values we convert to strings to workaround Chai + // limitations when dealing with nested arrays (required for BNs) + const indexedValues = await Promise.all(Array(values.length).fill().map((_, index) => set.at(index))); + expect( + indexedValues.map(v => v.toString()), + ).to.have.same.members( + values.map(v => v.toString()), + ); + + const returnedValues = await set.values(); + expect( + returnedValues.map(v => v.toString()), + ).to.have.same.members( + values.map(v => v.toString()), + ); + } + + it('starts empty', async function () { + expect(await this.set.contains(valueA)).to.equal(false); + + await expectMembersMatch(this.set, []); + }); + + describe('add', function () { + it('adds a value', async function () { + const receipt = await this.set.add(valueA); + expectEvent(receipt, 'OperationResult', { result: true }); + + await expectMembersMatch(this.set, [valueA]); + }); + + it('adds several values', async function () { + await this.set.add(valueA); + await this.set.add(valueB); + + await expectMembersMatch(this.set, [valueA, valueB]); + expect(await this.set.contains(valueC)).to.equal(false); + }); + + it('returns false when adding values already in the set', async function () { + await this.set.add(valueA); + + const receipt = (await this.set.add(valueA)); + expectEvent(receipt, 'OperationResult', { result: false }); + + await expectMembersMatch(this.set, [valueA]); + }); + }); + + describe('at', function () { + it('reverts when retrieving non-existent elements', async function () { + await expectRevert.unspecified(this.set.at(0)); + }); + }); + + describe('remove', function () { + it('removes added values', async function () { + await this.set.add(valueA); + + const receipt = await this.set.remove(valueA); + expectEvent(receipt, 'OperationResult', { result: true }); + + expect(await this.set.contains(valueA)).to.equal(false); + await expectMembersMatch(this.set, []); + }); + + it('returns false when removing values not in the set', async function () { + const receipt = await this.set.remove(valueA); + expectEvent(receipt, 'OperationResult', { result: false }); + + expect(await this.set.contains(valueA)).to.equal(false); + }); + + it('adds and removes multiple values', async function () { + // [] + + await this.set.add(valueA); + await this.set.add(valueC); + + // [A, C] + + await this.set.remove(valueA); + await this.set.remove(valueB); + + // [C] + + await this.set.add(valueB); + + // [C, B] + + await this.set.add(valueA); + await this.set.remove(valueC); + + // [A, B] + + await this.set.add(valueA); + await this.set.add(valueB); + + // [A, B] + + await this.set.add(valueC); + await this.set.remove(valueA); + + // [B, C] + + await this.set.add(valueA); + await this.set.remove(valueB); + + // [A, C] + + await expectMembersMatch(this.set, [valueA, valueC]); + + expect(await this.set.contains(valueB)).to.equal(false); + }); + }); +} + +module.exports = { + shouldBehaveLikeSet, +}; diff --git a/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.test.js b/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.test.js new file mode 100644 index 0000000..2b7d0a3 --- /dev/null +++ b/lib/openzeppelin-contracts/test/utils/structs/EnumerableSet.test.js @@ -0,0 +1,46 @@ +const { BN } = require('@openzeppelin/test-helpers'); + +const EnumerableBytes32SetMock = artifacts.require('EnumerableBytes32SetMock'); +const EnumerableAddressSetMock = artifacts.require('EnumerableAddressSetMock'); +const EnumerableUintSetMock = artifacts.require('EnumerableUintSetMock'); + +const { shouldBehaveLikeSet } = require('./EnumerableSet.behavior'); + +contract('EnumerableSet', function (accounts) { + // Bytes32Set + describe('EnumerableBytes32Set', function () { + const bytesA = '0xdeadbeef'.padEnd(66, '0'); + const bytesB = '0x0123456789'.padEnd(66, '0'); + const bytesC = '0x42424242'.padEnd(66, '0'); + + beforeEach(async function () { + this.set = await EnumerableBytes32SetMock.new(); + }); + + shouldBehaveLikeSet(bytesA, bytesB, bytesC); + }); + + // AddressSet + describe('EnumerableAddressSet', function () { + const [accountA, accountB, accountC] = accounts; + + beforeEach(async function () { + this.set = await EnumerableAddressSetMock.new(); + }); + + shouldBehaveLikeSet(accountA, accountB, accountC); + }); + + // UintSet + describe('EnumerableUintSet', function () { + const uintA = new BN('1234'); + const uintB = new BN('5678'); + const uintC = new BN('9101112'); + + beforeEach(async function () { + this.set = await EnumerableUintSetMock.new(); + }); + + shouldBehaveLikeSet(uintA, uintB, uintC); + }); +}); diff --git a/lib/uniswap/v3-core/.gitattributes b/lib/uniswap/v3-core/.gitattributes new file mode 100644 index 0000000..7cc88f0 --- /dev/null +++ b/lib/uniswap/v3-core/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity \ No newline at end of file diff --git a/lib/uniswap/v3-core/.github/stale.yml b/lib/uniswap/v3-core/.github/stale.yml new file mode 100644 index 0000000..c94c52d --- /dev/null +++ b/lib/uniswap/v3-core/.github/stale.yml @@ -0,0 +1,25 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +issues: + # Number of days of inactivity before an Issue or Pull Request becomes stale + daysUntilStale: 7 + + # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. + # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. + daysUntilClose: 7 + + # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) + onlyLabels: + - question + - autoclose + + # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable + exemptLabels: + - p0 + - bug + + # Comment to post when marking as stale. Set to `false` to disable + markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. diff --git a/lib/uniswap/v3-core/.github/workflows/fuzz-testing.yml b/lib/uniswap/v3-core/.github/workflows/fuzz-testing.yml new file mode 100644 index 0000000..bf3892e --- /dev/null +++ b/lib/uniswap/v3-core/.github/workflows/fuzz-testing.yml @@ -0,0 +1,60 @@ +name: Fuzz Testing + +on: + push: + branches: + - main + pull_request: + +jobs: + echidna: + name: Echidna + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + testName: + - TickBitmapEchidnaTest + - TickMathEchidnaTest + - SqrtPriceMathEchidnaTest + - SwapMathEchidnaTest + - TickEchidnaTest + - TickOverflowSafetyEchidnaTest + - OracleEchidnaTest + - BitMathEchidnaTest + - LowGasSafeMathEchidnaTest + - UnsafeMathEchidnaTest + - FullMathEchidnaTest + + steps: + - uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v1 + with: + node-version: 12 + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install node dependencies + run: yarn install --frozen-lockfile + + - name: Install pip3 + run: | + python -m pip install --upgrade pip + + - name: Install slither + run: | + pip3 install slither-analyzer + + - name: Install echidna + run: | + sudo wget -O /tmp/echidna-test.tar.gz https://github.com/crytic/echidna/releases/download/v1.6.0/echidna-test-v1.6.0-Ubuntu-18.04.tar.gz + sudo tar -xf /tmp/echidna-test.tar.gz -C /usr/bin + sudo chmod +x /usr/bin/echidna-test + + - name: Run ${{ matrix.testName }} + run: echidna-test . --contract ${{ matrix.testName }} --config echidna.config.yml diff --git a/lib/uniswap/v3-core/.github/workflows/lint.yml b/lib/uniswap/v3-core/.github/workflows/lint.yml new file mode 100644 index 0000000..942d420 --- /dev/null +++ b/lib/uniswap/v3-core/.github/workflows/lint.yml @@ -0,0 +1,32 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + +jobs: + run-linters: + name: Run linters + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v1 + with: + node-version: 12 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run linters + uses: wearerequired/lint-action@a8497ddb33fb1205941fd40452ca9fff07e0770d + with: + github_token: ${{ secrets.github_token }} + prettier: true + auto_fix: true + prettier_extensions: 'css,html,js,json,jsx,md,sass,scss,ts,tsx,vue,yaml,yml,sol' diff --git a/lib/uniswap/v3-core/.github/workflows/mythx.yml b/lib/uniswap/v3-core/.github/workflows/mythx.yml new file mode 100644 index 0000000..ffe713f --- /dev/null +++ b/lib/uniswap/v3-core/.github/workflows/mythx.yml @@ -0,0 +1,65 @@ +name: Mythx + +on: + workflow_dispatch: + +jobs: + mythx: + name: Submit to Mythx + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v1 + with: + node-version: 12 + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install node dependencies + run: yarn install --frozen-lockfile + + - name: Install pip3 + run: | + python -m pip install --upgrade pip + + - name: Install mythx CLI + run: | + pip3 install mythx-cli + + - name: Install solc-select + run: | + pip3 install solc-select + + - name: Install solc 0.7.6 + run: | + solc-select install 0.7.6 + solc-select use 0.7.6 + + - name: Submit code to Mythx + run: | + mythx --api-key ${{ secrets.MYTHX_API_KEY }} \ + --yes \ + analyze \ + --mode deep \ + --async \ + --create-group \ + --group-name "@uniswap/v3-core@${{ github.sha }}" \ + --solc-version 0.7.6 \ + --check-properties \ + contracts/test/TickBitmapEchidnaTest.sol --include TickBitmapEchidnaTest \ + contracts/test/TickMathEchidnaTest.sol --include TickMathEchidnaTest \ + contracts/test/SqrtPriceMathEchidnaTest.sol --include SqrtPriceMathEchidnaTest \ + contracts/test/SwapMathEchidnaTest.sol --include SwapMathEchidnaTest \ + contracts/test/TickEchidnaTest.sol --include TickEchidnaTest \ + contracts/test/TickOverflowSafetyEchidnaTest.sol --include TickOverflowSafetyEchidnaTest \ + contracts/test/OracleEchidnaTest.sol --include OracleEchidnaTest \ + contracts/test/BitMathEchidnaTest.sol --include BitMathEchidnaTest \ + contracts/test/LowGasSafeMathEchidnaTest.sol --include LowGasSafeMathEchidnaTest \ + contracts/test/UnsafeMathEchidnaTest.sol --include UnsafeMathEchidnaTest \ + contracts/test/FullMathEchidnaTest.sol --include FullMathEchidnaTest diff --git a/lib/uniswap/v3-core/.github/workflows/tests.yml b/lib/uniswap/v3-core/.github/workflows/tests.yml new file mode 100644 index 0000000..89567f9 --- /dev/null +++ b/lib/uniswap/v3-core/.github/workflows/tests.yml @@ -0,0 +1,38 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + +jobs: + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 12.x + + - id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- + + - name: Install dependencies + run: yarn install --frozen-lockfile + + # This is required separately from yarn test because it generates the typechain definitions + - name: Compile + run: yarn compile + + - name: Run unit tests + run: yarn test diff --git a/lib/uniswap/v3-core/.gitignore b/lib/uniswap/v3-core/.gitignore new file mode 100644 index 0000000..86e6f21 --- /dev/null +++ b/lib/uniswap/v3-core/.gitignore @@ -0,0 +1,5 @@ +artifacts/ +cache/ +crytic-export/ +node_modules/ +typechain/ diff --git a/lib/uniswap/v3-core/.prettierrc b/lib/uniswap/v3-core/.prettierrc new file mode 100644 index 0000000..31ba22d --- /dev/null +++ b/lib/uniswap/v3-core/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/lib/uniswap/v3-core/.solhint.json b/lib/uniswap/v3-core/.solhint.json new file mode 100644 index 0000000..11b3647 --- /dev/null +++ b/lib/uniswap/v3-core/.solhint.json @@ -0,0 +1,6 @@ +{ + "plugins": ["prettier"], + "rules": { + "prettier/prettier": "error" + } +} diff --git a/lib/uniswap/v3-core/.yarnrc b/lib/uniswap/v3-core/.yarnrc new file mode 100644 index 0000000..5455c6c --- /dev/null +++ b/lib/uniswap/v3-core/.yarnrc @@ -0,0 +1 @@ +ignore-scripts true diff --git a/lib/uniswap/v3-core/LICENSE b/lib/uniswap/v3-core/LICENSE new file mode 100644 index 0000000..075a13a --- /dev/null +++ b/lib/uniswap/v3-core/LICENSE @@ -0,0 +1,100 @@ +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Uniswap Labs + +Licensed Work: Uniswap V3 Core + The Licensed Work is (c) 2021 Uniswap Labs + +Additional Use Grant: Any uses listed and defined at + v3-core-license-grants.uniswap.eth + +Change Date: The earlier of 2023-04-01 or a date specified at + v3-core-license-date.uniswap.eth + +Change License: GNU General Public License v2.0 or later + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/lib/uniswap/v3-core/README.md b/lib/uniswap/v3-core/README.md new file mode 100644 index 0000000..e4d12e4 --- /dev/null +++ b/lib/uniswap/v3-core/README.md @@ -0,0 +1,66 @@ +# Uniswap V3 + +[![Lint](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/lint.yml/badge.svg)](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/lint.yml) +[![Tests](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/tests.yml/badge.svg)](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/tests.yml) +[![Fuzz Testing](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/fuzz-testing.yml/badge.svg)](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/fuzz-testing.yml) +[![Mythx](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/mythx.yml/badge.svg)](https://github.com/Uniswap/uniswap-v3-core/actions/workflows/mythx.yml) +[![npm version](https://img.shields.io/npm/v/@uniswap/v3-core/latest.svg)](https://www.npmjs.com/package/@uniswap/v3-core/v/latest) + +This repository contains the core smart contracts for the Uniswap V3 Protocol. +For higher level contracts, see the [uniswap-v3-periphery](https://github.com/Uniswap/uniswap-v3-periphery) +repository. + +## Bug bounty + +This repository is subject to the Uniswap V3 bug bounty program, per the terms defined [here](./bug-bounty.md). + +## Local deployment + +In order to deploy this code to a local testnet, you should install the npm package +`@uniswap/v3-core` +and import the factory bytecode located at +`@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json`. +For example: + +```typescript +import { + abi as FACTORY_ABI, + bytecode as FACTORY_BYTECODE, +} from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' + +// deploy the bytecode +``` + +This will ensure that you are testing against the same bytecode that is deployed to +mainnet and public testnets, and all Uniswap code will correctly interoperate with +your local deployment. + +## Using solidity interfaces + +The Uniswap v3 interfaces are available for import into solidity smart contracts +via the npm artifact `@uniswap/v3-core`, e.g.: + +```solidity +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +contract MyContract { + IUniswapV3Pool pool; + + function doSomethingWithPool() { + // pool.swap(...); + } +} + +``` + +## Licensing + +The primary license for Uniswap V3 Core is the Business Source License 1.1 (`BUSL-1.1`), see [`LICENSE`](./LICENSE). However, some files are dual licensed under `GPL-2.0-or-later`: + +- All files in `contracts/interfaces/` may also be licensed under `GPL-2.0-or-later` (as indicated in their SPDX headers), see [`contracts/interfaces/LICENSE`](./contracts/interfaces/LICENSE) +- Several files in `contracts/libraries/` may also be licensed under `GPL-2.0-or-later` (as indicated in their SPDX headers), see [`contracts/libraries/LICENSE`](contracts/libraries/LICENSE) + +### Other Exceptions + +- `contracts/libraries/FullMath.sol` is licensed under `MIT` (as indicated in its SPDX header), see [`contracts/libraries/LICENSE_MIT`](contracts/libraries/LICENSE_MIT) +- All files in `contracts/test` remain unlicensed (as indicated in their SPDX headers). diff --git a/lib/uniswap/v3-core/audits/abdk/audit.pdf b/lib/uniswap/v3-core/audits/abdk/audit.pdf new file mode 100644 index 0000000..d851413 Binary files /dev/null and b/lib/uniswap/v3-core/audits/abdk/audit.pdf differ diff --git a/lib/uniswap/v3-core/audits/tob/README.md b/lib/uniswap/v3-core/audits/tob/README.md new file mode 100644 index 0000000..5e4b55e --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/README.md @@ -0,0 +1,166 @@ +The following contains the properties written by Trail of Bits. + +- [End to End testing with Echidna](#end-to-end-testing-with-echidna) +- [Verification with Manticore](#verification-with-manticore) + +# End to End testing with Echidna + +We've implemented end-to-end properties for the Uniswap V3 Core pool contract. + +## Installation + +In order to run this, you need to install [echidna 1.7.0](https://github.com/crytic/echidna/releases/tag/v1.7.0). + +## Run + +Assuming you're in the root of the repo + +``` +echidna-test contracts/crytic/echidna/E2E_swap.sol --config contracts/crytic/echidna/E2E_swap.config.yaml --contract E2E_swap + +echidna-test contracts/crytic/echidna/E2E_mint_burn.sol --config contracts/crytic/echidna/E2E_mint_burn.config.yaml --contract E2E_mint_burn + +echidna-test contracts/crytic/echidna/Other.sol --config contracts/crytic/echidna/Other.config.yaml --contract Other +``` + +## Random but valid pool initialisation, created positions, and priceLimits + +To help Echidna to get good coverage we've used multiple helper functions to: + +- create random but valid pool initialization params (fee, tickSpacing, initial price) before doing swaps or mint/burn [link](./E2E_mint_burn.sol#L303-L337) [link](./E2E_swap.sol#L196-L230) +- create a random number of random but valid positions before testing swaps [link](./E2E_swap.sol#L233-L283) +- create a random but valid priceLimit when doing swap [link](./E2E_swap.sol#L68-L80) +- create random but valid position params when doing mint [link](./E2E_mint_burn.sol#L102-L130) + +By doing the above Echidna will be able to test the actual properties we want to test instead of bashing it's head against using an invalid priceLimit or invalid position params. The above also allows the creation of a dynamic number of random positions before executing swaps instead of using a static list. + +To achieve the above random but valid outcomes we use the `uint128 _amount` of swap/mint/burn as a seed to create randomness in the helper functions. This also means that retrieving the exact used params is not very straightforward. However, through a combination of hardhat `console.sol` and writing a small js unit test it's possible to retrieve the exact used params of every (list of) call(s). + +## Adjust hardhat.config.ts + +The Echidna contracts cost too much gas to deploy due to all the calls inside the constructor. + +Adjust the `hardhat.config.ts` to: + +```json +hardhat: { + allowUnlimitedContractSize: true, + gas: 950000000, + blockGasLimit: 950000000, + gasPrice: 1, +}, +``` + +### E2E_swap: retrieving the pool initialisation params and created positions + +The pool initialisation and created positions is deterministic and there is a `view` function that will return whatever a specific `_amount` (used as `_seed`) will create. + +Write a js unit test: + +```js +console.log(await E2E_swap.viewRandomInit('')) +``` + +### E2E_swap: retrieving the used priceLimit of a swap + +The priceLimit depends on the state of the pool contract, it is therefore easiest to retrieve by using hardhat's `console.sol`. + +Uncomment the following lines: + +- at the top of `E2E_swap.sol`: `// import 'hardhat/console.sol';` +- inside the swap functions: `// console.log('sqrtPriceLimitX96 = %s', sqrtPriceLimitX96); ` + +Instead of just one swap call, imagine Echidna reports two swap calls, and the second one causes an assertion failure. + +```js +// to get pool params + created positions +console.log(await E2E_swap.viewRandomInit('')) + +// execute the swap, which will create the above and log the used priceLimit to the console +await E2E_swap.test_swap_exactOut_oneForZero('') + +// execute the swap, logs the used priceLimit to the console +await E2E_swap.test_swap_exactIn_oneForZero('') +``` + +### E2E_mint_burn: retrieving the pool initialisation params + +The pool initialisation params creation is deterministic and there is a `view` function that will return whatever a specific `_amount` (used as `_seed`) will create. + +Write a js unit test: + +```js +console.log(await E2E_mint_burn.viewInitRandomPoolParams('')) +``` + +### E2E_mint_burn: retrieving a mint's created random position + +```js +const poolInitParams = await E2E_mint_burn.viewInitRandomPoolParams('') + +const positionParams = await E2E_mint_burn.viewMintRandomNewPosition( + '', + poolInitParams.tickSpacing, + poolInitParams.tickCount, + poolInitParams.maxTick +) + +console.log(positionParams) +``` + +### E2E_mint_burn: retrieving a burn's used position + +Uncomment the following lines: + +- at the top of `E2E_mint_burn.sol`: `// import 'hardhat/console.sol';` +- inside the particular burn function: `// console.log('burn posIdx = %s', posIdx);` +- if this is a partial burn, also want to see the burned amount. inside the `test_burn_partial` function: `// console.log('burn amount = %s', burnAmount);` + +Then execute the burn in a js test. + +```js +// show pool init params +const poolInitParams = await E2E_mint_burn.viewInitRandomPoolParams('') +console.log(positionParams) + +// show pool mint position params +const positionParams = await E2E_mint_burn.viewMintRandomNewPosition( + '', + poolInitParams.tickSpacing, + poolInitParams.tickCount, + poolInitParams.maxTick +) +console.log(positionParams) + +// execute the first mint +await E2E_mint_burn.test_mint('') + +// execute the burn +await E2E_mint_burn.test_burn_partial('') +// this should log the index of the position that was burned to the console +// as well as the amount that was burned. +// together with the above output this should make it clear which exact position +// was burned and how much +``` + +# Verification with Manticore + +The verification was performed with the experimental branch [dev-evm-experiments](https://github.com/trailofbits/manticore/tree/dev-evm-experiments), which contains new optimizations and is a work in progress. Trail of Bits will ensure that the following properties hold once the branch has stabilized and been included in a Manticore release. + +For conveniance, we followed the pattern "if there is reacheable path, there is a bug". + +To verify a property, run: + +``` +manticore . --contract CONTRACT_NAME --txlimit 1 --smt.solver all --quick-mode --lazy-evaluation --core.procs 1 +``` + +> The command might change once `dev-evm-experiments` has stabilized + +Manticore will create a `mcore_X` directory. If no `X.tx` file is generated, it means that Manticore did not find a path violating the property. + +| ID | Description | Contract | Status | +| --- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | -------- | +| 01 | `BitMath.mostSignificantBit returns a value in x >= 2**msb && (msb == 255 or x < 2**(msb+1)).` | [`VerifyBitMathMsb`](./contracts/crytic/manticore/001.sol) | Verified | +| 02 | `BitMath.leastSignificantBit returns a value in ((x & 2** lsb) != 0) && ((x & (2**(lsb -1))) == 0).` | [`VerifyBitMathLsb`](./contracts/crytic/manticore/002.sol) | Verified | +| 03 | `If LiquidityMath.addDelta returns, the value will be equal to x + uint128(y).` | [`VerifyLiquidityMathAddDelta`](./contracts/crytic/manticore/003.sol) | Verified | diff --git a/lib/uniswap/v3-core/audits/tob/audit.pdf b/lib/uniswap/v3-core/audits/tob/audit.pdf new file mode 100644 index 0000000..7a7f0dc Binary files /dev/null and b/lib/uniswap/v3-core/audits/tob/audit.pdf differ diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_mint_burn.config.yaml b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_mint_burn.config.yaml new file mode 100644 index 0000000..37472eb --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_mint_burn.config.yaml @@ -0,0 +1,16 @@ +checkAsserts: true +coverage: true +codeSize: 0x60000 +corpusDir: echidna_e2e_mint_burn_corpus +seqLen: 10 +testLimit: 100000 +timeout: 600 # 10 minutes + +# blacklist +filterFunctions: + [ + 'E2E_mint_burn.viewInitRandomPoolParams(uint128)', + 'E2E_mint_burn.viewMintRandomNewPosition(uint128,int24,uint24,int24)', + 'E2E_mint_burn.viewBurnRandomPositionIdx(uint128,uint128)', + 'E2E_mint_burn.viewBurnRandomPositionBurnAmount(uint128,uint128)', + ] diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_mint_burn.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_mint_burn.sol new file mode 100644 index 0000000..e33d6a2 --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_mint_burn.sol @@ -0,0 +1,510 @@ +pragma solidity =0.7.6; +pragma abicoder v2; + +import './Setup.sol'; +import '../../../../../contracts/test/TestERC20.sol'; +import '../../../../../contracts/libraries/TickMath.sol'; +import '../../../../../contracts/UniswapV3Pool.sol'; +import '../../../../../contracts/libraries/Position.sol'; + +// import 'hardhat/console.sol'; + +contract E2E_mint_burn { + SetupTokens tokens; + SetupUniswap uniswap; + + UniswapV3Pool pool; + + TestERC20 token0; + TestERC20 token1; + + UniswapMinter minter; + UniswapSwapper swapper; + + int24[] usedTicks; + bool inited; + + PoolPosition[] positions; + + struct PoolPosition { + int24 tickLower; + int24 tickUpper; + uint128 amount; + bytes32 key; + } + + struct PoolParams { + uint24 fee; + int24 tickSpacing; + int24 minTick; + int24 maxTick; + uint24 tickCount; + uint160 startPrice; + int24 startTick; + } + + PoolParams poolParams; + + constructor() public { + tokens = new SetupTokens(); + token0 = tokens.token0(); + token1 = tokens.token1(); + + uniswap = new SetupUniswap(token0, token1); + + minter = new UniswapMinter(token0, token1); + swapper = new UniswapSwapper(token0, token1); + + tokens.mintTo(0, address(minter), 1e10 ether); + tokens.mintTo(1, address(minter), 1e10 ether); + } + + // + // + // Helpers + // + // + + function _getRandomPositionIdx(uint128 _seed, uint256 _positionsCount) internal view returns (uint128 positionIdx) { + positionIdx = _seed % uint128(_positionsCount); + } + + function _getRandomBurnAmount(uint128 _seed, uint128 _positionAmount) internal view returns (uint128 burnAmount) { + burnAmount = _seed % _positionAmount; + require(burnAmount < _positionAmount); + require(burnAmount > 0); + } + + function _getRandomPositionIdxAndBurnAmount(uint128 _seed) + internal + view + returns (uint128 positionIdx, uint128 burnAmount) + { + positionIdx = _getRandomPositionIdx(_seed, positions.length); + burnAmount = _getRandomBurnAmount(_seed, positions[positionIdx].amount); + } + + // adds all lower and upper ticks to an array such that the liquidity(Net) invariants + // can loop over them + function storeUsedTicks(int24 _tL, int24 _tU) internal { + bool lowerAlreadyUsed = false; + bool upperAlreadyUsed = false; + for (uint8 j = 0; j < usedTicks.length; j++) { + if (usedTicks[j] == _tL) lowerAlreadyUsed = true; + else if (usedTicks[j] == _tU) upperAlreadyUsed = true; + } + if (!lowerAlreadyUsed) usedTicks.push(_tL); + if (!upperAlreadyUsed) usedTicks.push(_tU); + } + + function removePosition(uint256 _posIdx) internal { + positions[_posIdx] = positions[positions.length - 1]; + positions.pop(); + } + + // use the _amount as _seed to create a random but valid position + function forgePosition( + uint128 _seed, + int24 _poolTickSpacing, + uint24 _poolTickCount, + int24 _poolMaxTick + ) internal view returns (int24 tickLower, int24 tickUpper) { + int24 randomTick1 = int24((_seed % uint128(_poolTickCount)) * uint128(_poolTickSpacing)); + + if (_seed % 2 == 0) { + // make tickLower positive + tickLower = randomTick1; + + // tickUpper is somewhere above tickLower + uint24 poolTickCountLeft = uint24((_poolMaxTick - randomTick1) / _poolTickSpacing); + int24 randomTick2 = int24((_seed % uint128(poolTickCountLeft)) * uint128(_poolTickSpacing)); + tickUpper = tickLower + randomTick2; + } else { + // make tickLower negative or zero + tickLower = randomTick1 == 0 ? 0 : -randomTick1; + + uint24 poolTickCountNegativeLeft = uint24((_poolMaxTick - randomTick1) / _poolTickSpacing); + uint24 poolTickCountTotalLeft = poolTickCountNegativeLeft + _poolTickCount; + + uint24 randomIncrement = uint24((_seed % uint128(poolTickCountTotalLeft)) * uint128(_poolTickSpacing)); + + if (randomIncrement <= uint24(tickLower)) { + // tickUpper will also be negative + tickUpper = tickLower + int24(randomIncrement); + } else { + // tickUpper is positive + randomIncrement -= uint24(-tickLower); + tickUpper = tickLower + int24(randomIncrement); + } + } + } + + // + // + // Invariants + // + // + + function check_liquidityNet_invariant() internal { + int128 liquidityNet = 0; + for (uint256 i = 0; i < usedTicks.length; i++) { + (, int128 tickLiquidityNet, , ) = pool.ticks(usedTicks[i]); + int128 result = liquidityNet + tickLiquidityNet; + assert( + (tickLiquidityNet >= 0 && result >= liquidityNet) || (tickLiquidityNet < 0 && result < liquidityNet) + ); + liquidityNet = result; + } + + // prop #20 + assert(liquidityNet == 0); + } + + function check_liquidity_invariant() internal { + (, int24 currentTick, , , , , ) = pool.slot0(); + + int128 liquidity = 0; + for (uint256 i = 0; i < usedTicks.length; i++) { + int24 tick = usedTicks[i]; + + if (tick <= currentTick) { + (, int128 tickLiquidityNet, , ) = pool.ticks(tick); + + int128 result = liquidity + tickLiquidityNet; + assert((tickLiquidityNet >= 0 && result >= liquidity) || (tickLiquidityNet < 0 && result < liquidity)); + liquidity = result; + } + } + + // prop #21 + assert(uint128(liquidity) == pool.liquidity()); + assert(liquidity >= 0); + } + + function check_tick_feegrowth_invariant() internal { + (, int24 currentTick, , , , , ) = pool.slot0(); + + if (currentTick == poolParams.maxTick || currentTick == poolParams.minTick) return; + + int24 tickBelow = currentTick - poolParams.tickSpacing; + int24 tickAbove = currentTick + poolParams.tickSpacing; + + (, , uint256 tB_feeGrowthOutside0X128, uint256 tB_feeGrowthOutside1X128) = pool.ticks(tickBelow); + (, , uint256 tA_feeGrowthOutside0X128, uint256 tA_feeGrowthOutside1X128) = pool.ticks(tickAbove); + + // prop #22 + assert(tB_feeGrowthOutside0X128 + tA_feeGrowthOutside0X128 <= pool.feeGrowthGlobal0X128()); + + // prop #23 + assert(tB_feeGrowthOutside1X128 + tA_feeGrowthOutside1X128 <= pool.feeGrowthGlobal1X128()); + } + + function check_mint_invariants( + int24 _tickLower, + int24 _tickUpper, + UniswapMinter.MinterStats memory bfre, + UniswapMinter.MinterStats memory aftr + ) internal { + (, int24 currentTick, , , , , ) = pool.slot0(); + + // prop #1 + if (currentTick >= _tickLower && currentTick < _tickUpper) { + assert(aftr.liq > bfre.liq); + } else { + assert(aftr.liq == bfre.liq); + } + + // prop #2 + assert(aftr.tL_liqGross > bfre.tL_liqGross); + + // prop #3 + assert(aftr.tU_liqGross > bfre.tU_liqGross); + + // prop #4 + assert(aftr.tL_liqNet > bfre.tL_liqNet); + + // prop #5 + assert(aftr.tU_liqNet < bfre.tU_liqNet); + } + + function check_burn_invariants( + uint128 _burnAmount, + int24 _tickLower, + int24 _tickUpper, + uint128 _newPosAmount, + UniswapMinter.MinterStats memory bfre, + UniswapMinter.MinterStats memory aftr + ) internal { + (, int24 currentTick, , , , , ) = pool.slot0(); + + if (_burnAmount > 0) { + // prop #7 + if (currentTick >= _tickLower && currentTick < _tickUpper) { + assert(aftr.liq < bfre.liq); + } else { + assert(aftr.liq == bfre.liq); + } + } else { + // prop #29 + assert(aftr.liq == bfre.liq); + } + + // prop #8 + assert(aftr.tL_liqGross <= bfre.tL_liqGross); + + // prop #9 + assert(aftr.tU_liqGross <= bfre.tU_liqGross); + + // prop #10 + assert(aftr.tL_liqNet <= bfre.tL_liqNet); + + // prop #11 + assert(aftr.tU_liqNet >= bfre.tU_liqNet); + + bytes32 positionKey = keccak256(abi.encodePacked(address(minter), _tickLower, _tickUpper)); + (uint128 positionLiquidity, , , , ) = pool.positions(positionKey); + + // prop #27 + assert(positionLiquidity == _newPosAmount); + } + + // + // + // Helper to reconstruct the "random" init setup of the pool + // + // + + function viewInitRandomPoolParams(uint128 _seed) public view returns (PoolParams memory _poolParams) { + _poolParams = forgePoolParams(_seed); + } + + function viewMintRandomNewPosition( + uint128 _seed, + int24 _poolTickSpacing, + uint24 _poolTickCount, + int24 _poolMaxTick + ) + public + view + returns ( + int24 tickLower, + int24 tickUpper, + uint128 amount + ) + { + (tickLower, tickUpper) = forgePosition(_seed, _poolTickSpacing, _poolTickCount, _poolMaxTick); + amount = _seed; + } + + function viewBurnRandomPositionIdx(uint128 _seed, uint128 _positionsCount) + public + view + returns (uint128 positionIdx) + { + positionIdx = _getRandomPositionIdx(_seed, _positionsCount); + } + + function viewBurnRandomPositionBurnAmount(uint128 _seed, uint128 _positionAmount) + public + view + returns (uint128 burnAmount) + { + burnAmount = _getRandomBurnAmount(_seed, _positionAmount); + } + + // + // + // Setup functions + // + // + + function forgePoolParams(uint128 _seed) internal view returns (PoolParams memory _poolParams) { + // + // decide on one of the three fees, and corresponding tickSpacing + // + if (_seed % 3 == 0) { + _poolParams.fee = uint24(500); + _poolParams.tickSpacing = int24(10); + } else if (_seed % 3 == 1) { + _poolParams.fee = uint24(3000); + _poolParams.tickSpacing = int24(60); + } else if (_seed % 3 == 2) { + _poolParams.fee = uint24(10000); + _poolParams.tickSpacing = int24(2000); + } + + _poolParams.maxTick = (int24(887272) / _poolParams.tickSpacing) * _poolParams.tickSpacing; + _poolParams.minTick = -_poolParams.maxTick; + _poolParams.tickCount = uint24(_poolParams.maxTick / _poolParams.tickSpacing); + + // + // set the initial price + // + _poolParams.startTick = int24((_seed % uint128(_poolParams.tickCount)) * uint128(_poolParams.tickSpacing)); + if (_seed % 3 == 0) { + // set below 0 + _poolParams.startPrice = TickMath.getSqrtRatioAtTick(-_poolParams.startTick); + } else if (_seed % 3 == 1) { + // set at 0 + _poolParams.startPrice = TickMath.getSqrtRatioAtTick(0); + _poolParams.startTick = 0; + } else if (_seed % 3 == 2) { + // set above 0 + _poolParams.startPrice = TickMath.getSqrtRatioAtTick(_poolParams.startTick); + } + } + + function _init(uint128 _seed) internal { + // + // generate random pool params + // + poolParams = forgePoolParams(_seed); + + // + // deploy the pool + // + uniswap.createPool(poolParams.fee, poolParams.startPrice); + pool = uniswap.pool(); + + // + // set the pool inside the minter and swapper contracts + // + minter.setPool(pool); + + inited = true; + } + + // + // + // Functions to fuzz + // + // + + function test_mint(uint128 _amount) public { + if (!inited) _init(_amount); + (int24 _tL, int24 _tU) = forgePosition( + _amount, + poolParams.tickSpacing, + poolParams.tickCount, + poolParams.maxTick + ); + + (UniswapMinter.MinterStats memory bfre, UniswapMinter.MinterStats memory aftr) = minter.doMint( + _tL, + _tU, + _amount + ); + storeUsedTicks(_tL, _tU); + + check_mint_invariants(_tL, _tU, bfre, aftr); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + + bytes32 positionKey = keccak256(abi.encodePacked(address(minter), _tL, _tU)); + + bool mintingToExistingPos = false; + for (uint256 i = 0; i < positions.length; i++) { + if (positions[i].key == positionKey) { + // minting to an existing position + positions[i].amount += _amount; + mintingToExistingPos = true; + break; + } + } + + if (!mintingToExistingPos) { + positions.push(PoolPosition(_tL, _tU, _amount, positionKey)); + } + } + + function test_burn_partial(uint128 _amount) public { + require(positions.length > 0); + + (uint128 posIdx, uint128 burnAmount) = _getRandomPositionIdxAndBurnAmount(_amount); + // console.log('burn posIdx = %s', posIdx); + // console.log('burn amount = %s', burnAmount); + PoolPosition storage pos = positions[posIdx]; + + UniswapMinter.MinterStats memory bfre; + UniswapMinter.MinterStats memory aftr; + + try minter.doBurn(pos.tickLower, pos.tickUpper, burnAmount) returns ( + UniswapMinter.MinterStats memory bfre_burn, + UniswapMinter.MinterStats memory aftr_burn + ) { + bfre = bfre_burn; + aftr = aftr_burn; + } catch { + // prop #28 + assert(false); + } + + check_burn_invariants(burnAmount, pos.tickLower, pos.tickUpper, pos.amount - burnAmount, bfre, aftr); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + + pos.amount = pos.amount - burnAmount; + } + + function test_burn_full(uint128 _amount) public { + require(positions.length > 0); + + uint128 posIdx = _getRandomPositionIdx(_amount, positions.length); + // console.log('burn posIdx = %s', posIdx); + PoolPosition storage pos = positions[posIdx]; + + UniswapMinter.MinterStats memory bfre; + UniswapMinter.MinterStats memory aftr; + + try minter.doBurn(pos.tickLower, pos.tickUpper, pos.amount) returns ( + UniswapMinter.MinterStats memory bfre_burn, + UniswapMinter.MinterStats memory aftr_burn + ) { + bfre = bfre_burn; + aftr = aftr_burn; + } catch { + // prop #25 + assert(false); + } + + check_burn_invariants(pos.amount, pos.tickLower, pos.tickUpper, 0, bfre, aftr); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + + removePosition(posIdx); + } + + function test_burn_zero(uint128 _amount) public { + require(positions.length > 0); + + uint128 posIdx = _getRandomPositionIdx(_amount, positions.length); + // console.log('burn posIdx = %s', posIdx); + PoolPosition storage pos = positions[posIdx]; + + UniswapMinter.MinterStats memory bfre; + UniswapMinter.MinterStats memory aftr; + + try minter.doBurn(pos.tickLower, pos.tickUpper, 0) returns ( + UniswapMinter.MinterStats memory bfre_burn, + UniswapMinter.MinterStats memory aftr_burn + ) { + bfre = bfre_burn; + aftr = aftr_burn; + } catch { + // prop #26 + assert(false); + } + + check_burn_invariants(0, pos.tickLower, pos.tickUpper, pos.amount, bfre, aftr); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + } +} diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_swap.config.yaml b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_swap.config.yaml new file mode 100644 index 0000000..b2c5c6d --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_swap.config.yaml @@ -0,0 +1,10 @@ +checkAsserts: true +coverage: true +codeSize: 0x60000 +corpusDir: echidna_e2e_swap_corpus +seqLen: 10 +testLimit: 100000 +timeout: 3600 # 1 hour + +# blacklist +filterFunctions: ['E2E_swap.viewRandomInit(uint128)'] diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_swap.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_swap.sol new file mode 100644 index 0000000..863c5dd --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/E2E_swap.sol @@ -0,0 +1,499 @@ +pragma solidity =0.7.6; +pragma abicoder v2; + +import './Setup.sol'; +import '../../../../../contracts/test/TestERC20.sol'; +import '../../../../../contracts/libraries/TickMath.sol'; +import '../../../../../contracts/UniswapV3Pool.sol'; + +// import 'hardhat/console.sol'; + +contract E2E_swap { + SetupTokens tokens; + SetupUniswap uniswap; + + UniswapV3Pool pool; + + TestERC20 token0; + TestERC20 token1; + + UniswapMinter minter; + UniswapSwapper swapper; + + int24[] usedTicks; + bool inited; + + struct PoolParams { + uint24 fee; + int24 tickSpacing; + int24 minTick; + int24 maxTick; + uint24 tickCount; + uint160 startPrice; + int24 startTick; + } + + struct PoolPositions { + int24[] tickLowers; + int24[] tickUppers; + uint128[] amounts; + } + + PoolParams poolParams; + PoolPositions poolPositions; + + constructor() public { + tokens = new SetupTokens(); + token0 = tokens.token0(); + token1 = tokens.token1(); + + uniswap = new SetupUniswap(token0, token1); + + minter = new UniswapMinter(token0, token1); + swapper = new UniswapSwapper(token0, token1); + + tokens.mintTo(0, address(swapper), 1e9 ether); + tokens.mintTo(1, address(swapper), 1e9 ether); + + tokens.mintTo(0, address(minter), 1e10 ether); + tokens.mintTo(1, address(minter), 1e10 ether); + } + + // + // + // Helpers + // + // + + function get_random_zeroForOne_priceLimit(int256 _amountSpecified) + internal + view + returns (uint160 sqrtPriceLimitX96) + { + // help echidna a bit by calculating a valid sqrtPriceLimitX96 using the amount as random seed + (uint160 currentPrice, , , , , , ) = pool.slot0(); + uint160 minimumPrice = TickMath.MIN_SQRT_RATIO; + sqrtPriceLimitX96 = + minimumPrice + + uint160( + (uint256(_amountSpecified > 0 ? _amountSpecified : -_amountSpecified) % (currentPrice - minimumPrice)) + ); + } + + function get_random_oneForZero_priceLimit(int256 _amountSpecified) + internal + view + returns (uint160 sqrtPriceLimitX96) + { + // help echidna a bit by calculating a valid sqrtPriceLimitX96 using the amount as random seed + (uint160 currentPrice, , , , , , ) = pool.slot0(); + uint160 maximumPrice = TickMath.MAX_SQRT_RATIO; + sqrtPriceLimitX96 = + currentPrice + + uint160( + (uint256(_amountSpecified > 0 ? _amountSpecified : -_amountSpecified) % (maximumPrice - currentPrice)) + ); + } + + // + // + // Invariants + // + // + + function check_liquidityNet_invariant() internal { + int128 liquidityNet = 0; + for (uint256 i = 0; i < usedTicks.length; i++) { + (, int128 tickLiquidityNet, , ) = pool.ticks(usedTicks[i]); + int128 result = liquidityNet + tickLiquidityNet; + assert( + (tickLiquidityNet >= 0 && result >= liquidityNet) || (tickLiquidityNet < 0 && result < liquidityNet) + ); + liquidityNet = result; + } + + // prop #20 + assert(liquidityNet == 0); + } + + function check_liquidity_invariant() internal { + (, int24 currentTick, , , , , ) = pool.slot0(); + int128 liquidity = 0; + for (uint256 i = 0; i < usedTicks.length; i++) { + int24 tick = usedTicks[i]; + if (tick <= currentTick) { + (, int128 tickLiquidityNet, , ) = pool.ticks(tick); + int128 result = liquidity + tickLiquidityNet; + assert((tickLiquidityNet >= 0 && result >= liquidity) || (tickLiquidityNet < 0 && result < liquidity)); + liquidity = result; + } + } + + // prop #21 + assert(uint128(liquidity) == pool.liquidity()); + assert(liquidity >= 0); + } + + function check_tick_feegrowth_invariant() internal { + (, int24 currentTick, , , , , ) = pool.slot0(); + + if (currentTick == poolParams.maxTick || currentTick == poolParams.minTick) return; + + int24 tickBelow = currentTick - poolParams.tickSpacing; + int24 tickAbove = currentTick + poolParams.tickSpacing; + + (, , uint256 tB_feeGrowthOutside0X128, uint256 tB_feeGrowthOutside1X128) = pool.ticks(tickBelow); + (, , uint256 tA_feeGrowthOutside0X128, uint256 tA_feeGrowthOutside1X128) = pool.ticks(tickAbove); + + // prop #22 + assert(tB_feeGrowthOutside0X128 + tA_feeGrowthOutside0X128 <= pool.feeGrowthGlobal0X128()); + + // prop #23 + assert(tB_feeGrowthOutside1X128 + tA_feeGrowthOutside1X128 <= pool.feeGrowthGlobal1X128()); + } + + function check_swap_invariants( + int24 tick_bfre, + int24 tick_aftr, + uint128 liq_bfre, + uint128 liq_aftr, + uint256 bal_sell_bfre, + uint256 bal_sell_aftr, + uint256 bal_buy_bfre, + uint256 bal_buy_aftr, + uint256 feegrowth_sell_bfre, + uint256 feegrowth_sell_aftr, + uint256 feegrowth_buy_bfre, + uint256 feegrowth_buy_aftr + ) internal { + // prop #17 + if (tick_bfre == tick_aftr) { + assert(liq_bfre == liq_aftr); + } + + // prop #13 + #15 + assert(feegrowth_sell_bfre <= feegrowth_sell_aftr); + + // prop #14 + #16 + assert(feegrowth_buy_bfre == feegrowth_buy_aftr); + + // prop #18 + #19 + if (bal_sell_bfre == bal_sell_aftr) { + assert(bal_buy_bfre == bal_buy_aftr); + } + } + + // + // + // Helper to reconstruct the "random" init setup of the pool + // + // + + function viewRandomInit(uint128 _seed) + public + view + returns (PoolParams memory poolParams, PoolPositions memory poolPositions) + { + poolParams = forgePoolParams(_seed); + poolPositions = forgePoolPositions(_seed, poolParams.tickSpacing, poolParams.tickCount, poolParams.maxTick); + } + + // + // + // Setup functions + // + // + + function forgePoolParams(uint128 _seed) internal view returns (PoolParams memory poolParams) { + // + // decide on one of the three fees, and corresponding tickSpacing + // + if (_seed % 3 == 0) { + poolParams.fee = uint24(500); + poolParams.tickSpacing = int24(10); + } else if (_seed % 3 == 1) { + poolParams.fee = uint24(3000); + poolParams.tickSpacing = int24(60); + } else if (_seed % 3 == 2) { + poolParams.fee = uint24(10000); + poolParams.tickSpacing = int24(2000); + } + + poolParams.maxTick = (int24(887272) / poolParams.tickSpacing) * poolParams.tickSpacing; + poolParams.minTick = -poolParams.maxTick; + poolParams.tickCount = uint24(poolParams.maxTick / poolParams.tickSpacing); + + // + // set the initial price + // + poolParams.startTick = int24((_seed % uint128(poolParams.tickCount)) * uint128(poolParams.tickSpacing)); + if (_seed % 3 == 0) { + // set below 0 + poolParams.startPrice = TickMath.getSqrtRatioAtTick(-poolParams.startTick); + } else if (_seed % 3 == 1) { + // set at 0 + poolParams.startPrice = TickMath.getSqrtRatioAtTick(0); + poolParams.startTick = 0; + } else if (_seed % 3 == 2) { + // set above 0 + poolParams.startPrice = TickMath.getSqrtRatioAtTick(poolParams.startTick); + } + } + + function forgePoolPositions( + uint128 _seed, + int24 _poolTickSpacing, + uint24 _poolTickCount, + int24 _poolMaxTick + ) internal view returns (PoolPositions memory poolPositions_) { + // between 1 and 10 (inclusive) positions + uint8 positionsCount = uint8(_seed % 10) + 1; + + poolPositions_.tickLowers = new int24[](positionsCount); + poolPositions_.tickUppers = new int24[](positionsCount); + poolPositions_.amounts = new uint128[](positionsCount); + + for (uint8 i = 0; i < positionsCount; i++) { + int24 tickLower; + int24 tickUpper; + uint128 amount; + + int24 randomTick1 = int24((_seed % uint128(_poolTickCount)) * uint128(_poolTickSpacing)); + + if (_seed % 2 == 0) { + // make tickLower positive + tickLower = randomTick1; + + // tickUpper is somewhere above tickLower + uint24 poolTickCountLeft = uint24((_poolMaxTick - randomTick1) / _poolTickSpacing); + int24 randomTick2 = int24((_seed % uint128(poolTickCountLeft)) * uint128(_poolTickSpacing)); + tickUpper = tickLower + randomTick2; + } else { + // make tickLower negative or zero + tickLower = randomTick1 == 0 ? 0 : -randomTick1; + + uint24 poolTickCountNegativeLeft = uint24((_poolMaxTick - randomTick1) / _poolTickSpacing); + uint24 poolTickCountTotalLeft = poolTickCountNegativeLeft + _poolTickCount; + + uint24 randomIncrement = uint24((_seed % uint128(poolTickCountTotalLeft)) * uint128(_poolTickSpacing)); + + if (randomIncrement <= uint24(tickLower)) { + // tickUpper will also be negative + tickUpper = tickLower + int24(randomIncrement); + } else { + // tickUpper is positive + randomIncrement -= uint24(-tickLower); + tickUpper = tickLower + int24(randomIncrement); + } + } + + amount = uint128(1e8 ether); + + poolPositions_.tickLowers[i] = tickLower; + poolPositions_.tickUppers[i] = tickUpper; + poolPositions_.amounts[i] = amount; + + _seed += uint128(tickLower); + } + } + + function _init(uint128 _seed) internal { + // + // generate random pool params + // + poolParams = forgePoolParams(_seed); + + // + // deploy the pool + // + uniswap.createPool(poolParams.fee, poolParams.startPrice); + pool = uniswap.pool(); + + // + // set the pool inside the minter and swapper contracts + // + minter.setPool(pool); + swapper.setPool(pool); + + // + // generate random positions + // + poolPositions = forgePoolPositions(_seed, poolParams.tickSpacing, poolParams.tickCount, poolParams.maxTick); + + // + // create the positions + // + for (uint8 i = 0; i < poolPositions.tickLowers.length; i++) { + int24 tickLower = poolPositions.tickLowers[i]; + int24 tickUpper = poolPositions.tickUppers[i]; + uint128 amount = poolPositions.amounts[i]; + + minter.doMint(tickLower, tickUpper, amount); + + bool lowerAlreadyUsed = false; + bool upperAlreadyUsed = false; + for (uint8 j = 0; j < usedTicks.length; j++) { + if (usedTicks[j] == tickLower) lowerAlreadyUsed = true; + else if (usedTicks[j] == tickUpper) upperAlreadyUsed = true; + } + if (!lowerAlreadyUsed) usedTicks.push(tickLower); + if (!upperAlreadyUsed) usedTicks.push(tickUpper); + } + + inited = true; + } + + // + // + // Functions to fuzz + // + // + + function test_swap_exactIn_zeroForOne(uint128 _amount) public { + require(_amount != 0); + + if (!inited) _init(_amount); + + require(token0.balanceOf(address(swapper)) >= uint256(_amount)); + int256 _amountSpecified = int256(_amount); + + uint160 sqrtPriceLimitX96 = get_random_zeroForOne_priceLimit(_amount); + // console.log('sqrtPriceLimitX96 = %s', sqrtPriceLimitX96); + + (UniswapSwapper.SwapperStats memory bfre, UniswapSwapper.SwapperStats memory aftr) = swapper.doSwap( + true, + _amountSpecified, + sqrtPriceLimitX96 + ); + + check_swap_invariants( + bfre.tick, + aftr.tick, + bfre.liq, + aftr.liq, + bfre.bal0, + aftr.bal0, + bfre.bal1, + aftr.bal1, + bfre.feeGrowthGlobal0X128, + aftr.feeGrowthGlobal0X128, + bfre.feeGrowthGlobal1X128, + aftr.feeGrowthGlobal1X128 + ); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + } + + function test_swap_exactIn_oneForZero(uint128 _amount) public { + require(_amount != 0); + + if (!inited) _init(_amount); + + require(token1.balanceOf(address(swapper)) >= uint256(_amount)); + int256 _amountSpecified = int256(_amount); + + uint160 sqrtPriceLimitX96 = get_random_oneForZero_priceLimit(_amount); + // console.log('sqrtPriceLimitX96 = %s', sqrtPriceLimitX96); + + (UniswapSwapper.SwapperStats memory bfre, UniswapSwapper.SwapperStats memory aftr) = swapper.doSwap( + false, + _amountSpecified, + sqrtPriceLimitX96 + ); + + check_swap_invariants( + bfre.tick, + aftr.tick, + bfre.liq, + aftr.liq, + bfre.bal1, + aftr.bal1, + bfre.bal0, + aftr.bal0, + bfre.feeGrowthGlobal1X128, + aftr.feeGrowthGlobal1X128, + bfre.feeGrowthGlobal0X128, + aftr.feeGrowthGlobal0X128 + ); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + } + + function test_swap_exactOut_zeroForOne(uint128 _amount) public { + require(_amount != 0); + + if (!inited) _init(_amount); + + require(token0.balanceOf(address(swapper)) > 0); + int256 _amountSpecified = -int256(_amount); + + uint160 sqrtPriceLimitX96 = get_random_zeroForOne_priceLimit(_amount); + // console.log('sqrtPriceLimitX96 = %s', sqrtPriceLimitX96); + + (UniswapSwapper.SwapperStats memory bfre, UniswapSwapper.SwapperStats memory aftr) = swapper.doSwap( + true, + _amountSpecified, + sqrtPriceLimitX96 + ); + + check_swap_invariants( + bfre.tick, + aftr.tick, + bfre.liq, + aftr.liq, + bfre.bal0, + aftr.bal0, + bfre.bal1, + aftr.bal1, + bfre.feeGrowthGlobal0X128, + aftr.feeGrowthGlobal0X128, + bfre.feeGrowthGlobal1X128, + aftr.feeGrowthGlobal1X128 + ); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + } + + function test_swap_exactOut_oneForZero(uint128 _amount) public { + require(_amount != 0); + + if (!inited) _init(_amount); + + require(token0.balanceOf(address(swapper)) > 0); + int256 _amountSpecified = -int256(_amount); + + uint160 sqrtPriceLimitX96 = get_random_oneForZero_priceLimit(_amount); + // console.log('sqrtPriceLimitX96 = %s', sqrtPriceLimitX96); + + (UniswapSwapper.SwapperStats memory bfre, UniswapSwapper.SwapperStats memory aftr) = swapper.doSwap( + false, + _amountSpecified, + sqrtPriceLimitX96 + ); + + check_swap_invariants( + bfre.tick, + aftr.tick, + bfre.liq, + aftr.liq, + bfre.bal1, + aftr.bal1, + bfre.bal0, + aftr.bal0, + bfre.feeGrowthGlobal1X128, + aftr.feeGrowthGlobal1X128, + bfre.feeGrowthGlobal0X128, + aftr.feeGrowthGlobal0X128 + ); + + check_liquidityNet_invariant(); + check_liquidity_invariant(); + check_tick_feegrowth_invariant(); + } +} diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Other.config.yaml b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Other.config.yaml new file mode 100644 index 0000000..0bad147 --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Other.config.yaml @@ -0,0 +1,7 @@ +checkAsserts: true +coverage: true +codeSize: 0x60000 +corpusDir: echidna_other_corpus +seqLen: 1000 +testLimit: 100000 +timeout: 3600 # 1 hour diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Other.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Other.sol new file mode 100644 index 0000000..5832e8b --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Other.sol @@ -0,0 +1,21 @@ +pragma solidity =0.7.6; + +import '../../../../../contracts/libraries/SqrtPriceMath.sol'; +import '../../../../../contracts/libraries/TickMath.sol'; + +contract Other { + // prop #30 + function test_getNextSqrtPriceFromInAndOutput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) public { + require(sqrtPX96 >= TickMath.MIN_SQRT_RATIO && sqrtPX96 < TickMath.MAX_SQRT_RATIO); + require(liquidity < 3121856577256316178563069792952001939); // max liquidity per tick + uint256 next_sqrt = SqrtPriceMath.getNextSqrtPriceFromInput(sqrtPX96, liquidity, amount, add); + assert(next_sqrt >= TickMath.MIN_SQRT_RATIO && next_sqrt < TickMath.MAX_SQRT_RATIO); + next_sqrt = SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtPX96, liquidity, amount, add); + assert(next_sqrt >= TickMath.MIN_SQRT_RATIO && next_sqrt < TickMath.MAX_SQRT_RATIO); + } +} diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Setup.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Setup.sol new file mode 100644 index 0000000..922838c --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/echidna/Setup.sol @@ -0,0 +1,205 @@ +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../../../../../contracts/test/TestERC20.sol'; +import '../../../../../contracts/UniswapV3Pool.sol'; +import '../../../../../contracts/UniswapV3Factory.sol'; + +contract SetupToken { + TestERC20 public token; + + constructor() public { + // this contract will receive the total supply of 100 tokens + token = new TestERC20(1e12 ether); + } + + function mintTo(address _recipient, uint256 _amount) public { + token.transfer(_recipient, _amount); + } +} + +contract SetupTokens { + SetupToken tokenSetup0; + SetupToken tokenSetup1; + + TestERC20 public token0; + TestERC20 public token1; + + constructor() public { + // create the token wrappers + tokenSetup0 = new SetupToken(); + tokenSetup1 = new SetupToken(); + + // switch them around so that token0's address is lower than token1's + // since this is what the uniswap factory will do when you create the pool + if (address(tokenSetup0.token()) > address(tokenSetup1.token())) { + (tokenSetup0, tokenSetup1) = (tokenSetup1, tokenSetup0); + } + + // save the erc20 tokens + token0 = tokenSetup0.token(); + token1 = tokenSetup1.token(); + } + + // mint either token0 or token1 to a chosen account + function mintTo( + uint256 _tokenIdx, + address _recipient, + uint256 _amount + ) public { + require(_tokenIdx == 0 || _tokenIdx == 1, 'invalid token idx'); + if (_tokenIdx == 0) tokenSetup0.mintTo(_recipient, _amount); + if (_tokenIdx == 1) tokenSetup1.mintTo(_recipient, _amount); + } +} + +contract SetupUniswap { + UniswapV3Pool public pool; + TestERC20 token0; + TestERC20 token1; + + // will create the following enabled fees and corresponding tickSpacing + // fee 500 + tickSpacing 10 + // fee 3000 + tickSpacing 60 + // fee 10000 + tickSpacing 200 + UniswapV3Factory factory; + + constructor(TestERC20 _token0, TestERC20 _token1) public { + factory = new UniswapV3Factory(); + token0 = _token0; + token1 = _token1; + } + + function createPool(uint24 _fee, uint160 _startPrice) public { + pool = UniswapV3Pool(factory.createPool(address(token0), address(token1), _fee)); + pool.initialize(_startPrice); + } +} + +contract UniswapMinter { + UniswapV3Pool pool; + TestERC20 token0; + TestERC20 token1; + + struct MinterStats { + uint128 liq; + uint128 tL_liqGross; + int128 tL_liqNet; + uint128 tU_liqGross; + int128 tU_liqNet; + } + + constructor(TestERC20 _token0, TestERC20 _token1) public { + token0 = _token0; + token1 = _token1; + } + + function setPool(UniswapV3Pool _pool) public { + pool = _pool; + } + + function uniswapV3MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external { + if (amount0Owed > 0) token0.transfer(address(pool), amount0Owed); + if (amount1Owed > 0) token1.transfer(address(pool), amount1Owed); + } + + function getTickLiquidityVars(int24 _tickLower, int24 _tickUpper) + internal + view + returns ( + uint128, + int128, + uint128, + int128 + ) + { + (uint128 tL_liqGross, int128 tL_liqNet, , ) = pool.ticks(_tickLower); + (uint128 tU_liqGross, int128 tU_liqNet, , ) = pool.ticks(_tickUpper); + return (tL_liqGross, tL_liqNet, tU_liqGross, tU_liqNet); + } + + function getStats(int24 _tickLower, int24 _tickUpper) internal view returns (MinterStats memory stats) { + (uint128 tL_lg, int128 tL_ln, uint128 tU_lg, int128 tU_ln) = getTickLiquidityVars(_tickLower, _tickUpper); + return MinterStats(pool.liquidity(), tL_lg, tL_ln, tU_lg, tU_ln); + } + + function doMint( + int24 _tickLower, + int24 _tickUpper, + uint128 _amount + ) public returns (MinterStats memory bfre, MinterStats memory aftr) { + bfre = getStats(_tickLower, _tickUpper); + pool.mint(address(this), _tickLower, _tickUpper, _amount, new bytes(0)); + aftr = getStats(_tickLower, _tickUpper); + } + + function doBurn( + int24 _tickLower, + int24 _tickUpper, + uint128 _amount + ) public returns (MinterStats memory bfre, MinterStats memory aftr) { + bfre = getStats(_tickLower, _tickUpper); + pool.burn(_tickLower, _tickUpper, _amount); + aftr = getStats(_tickLower, _tickUpper); + } +} + +contract UniswapSwapper { + UniswapV3Pool pool; + TestERC20 token0; + TestERC20 token1; + + struct SwapperStats { + uint128 liq; + uint256 feeGrowthGlobal0X128; + uint256 feeGrowthGlobal1X128; + uint256 bal0; + uint256 bal1; + int24 tick; + } + + constructor(TestERC20 _token0, TestERC20 _token1) public { + token0 = _token0; + token1 = _token1; + } + + function setPool(UniswapV3Pool _pool) public { + pool = _pool; + } + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external { + if (amount0Delta > 0) token0.transfer(address(pool), uint256(amount0Delta)); + if (amount1Delta > 0) token1.transfer(address(pool), uint256(amount1Delta)); + } + + function getStats() internal view returns (SwapperStats memory stats) { + (, int24 currentTick, , , , , ) = pool.slot0(); + return + SwapperStats( + pool.liquidity(), + pool.feeGrowthGlobal0X128(), + pool.feeGrowthGlobal1X128(), + token0.balanceOf(address(this)), + token1.balanceOf(address(this)), + currentTick + ); + } + + function doSwap( + bool _zeroForOne, + int256 _amountSpecified, + uint160 _sqrtPriceLimitX96 + ) public returns (SwapperStats memory bfre, SwapperStats memory aftr) { + bfre = getStats(); + pool.swap(address(this), _zeroForOne, _amountSpecified, _sqrtPriceLimitX96, new bytes(0)); + aftr = getStats(); + } +} diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/001.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/001.sol new file mode 100644 index 0000000..dada7c4 --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/001.sol @@ -0,0 +1,11 @@ +import '../../../../../contracts/libraries/BitMath.sol'; + +contract VerifyBitMathMsb { + function verify(uint256 x) external { + uint256 msb = BitMath.mostSignificantBit(x); + + bool property = x >= 2**msb && (msb == 255 || x < 2**(msb + 1)); + + require(!property); + } +} diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/002.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/002.sol new file mode 100644 index 0000000..055bc92 --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/002.sol @@ -0,0 +1,12 @@ +import '../../../../../contracts/libraries/BitMath.sol'; + +contract VerifyBitMathLsb { + function verify(uint256 x) external { + uint256 lsb = BitMath.leastSignificantBit(x); + + // (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) + bool property = ((x & (2**lsb)) != 0) && ((x & (2**(lsb - 1))) == 0); + + require(!property); + } +} diff --git a/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/003.sol b/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/003.sol new file mode 100644 index 0000000..99cee9f --- /dev/null +++ b/lib/uniswap/v3-core/audits/tob/contracts/crytic/manticore/003.sol @@ -0,0 +1,9 @@ +import '../../../../../contracts/libraries/LiquidityMath.sol'; + +contract VerifyLiquidityMathAddDelta { + function verify(uint128 x, int128 y) external { + uint256 z = LiquidityMath.addDelta(x, y); + + require(z != x + uint128(y)); + } +} diff --git a/lib/uniswap/v3-core/bug-bounty.md b/lib/uniswap/v3-core/bug-bounty.md new file mode 100644 index 0000000..f01c4c6 --- /dev/null +++ b/lib/uniswap/v3-core/bug-bounty.md @@ -0,0 +1,76 @@ +# Uniswap V3 Bug Bounty + +## Overview + +Starting on March 23rd, 2021, the [uniswap-v3-core](https://github.com/Uniswap/uniswap-v3-core) repository is subject to the Uniswap V3 Bug Bounty (the “Program”) to incentivize responsible bug disclosure. + +We are limiting the scope of the Program to critical and high severity bugs, and are offering a reward of up to $500,000. Happy hunting! + +## Scope + +The scope of the Program is limited to bugs that result in the draining of contract funds. + +The following are not within the scope of the Program: + +- Any contract located under [contracts/test](./contracts/test). +- Bugs in any third party contract or platform that interacts with Uniswap V3. +- Vulnerabilities already reported and/or discovered in contracts built by third parties on Uniswap V3. +- Any already-reported bugs. + +Vulnerabilities contingent upon the occurrence of any of the following also are outside the scope of this Program: + +- Frontend bugs +- DDOS attacks +- Spamming +- Phishing +- Automated tools (Github Actions, AWS, etc.) +- Compromise or misuse of third party systems or services + +## Assumptions + +Uniswap V3 was developed with the following assumptions, and thus any bug must also adhere to the following assumptions +to be eligible for the bug bounty: + +- The total supply of any token does not exceed 2128 - 1, i.e. `type(uint128).max`. +- The `transfer` and `transferFrom` methods of any token strictly decrease the balance of the token sender by the transfer amount and increases the balance of token recipient by the transfer amount, i.e. fee on transfer tokens are excluded. +- The token balance of an address can only change due to a call to `transfer` by the sender or `transferFrom` by an approved address, i.e. rebase tokens and interest bearing tokens are excluded. + +## Rewards + +Rewards will be allocated based on the severity of the bug disclosed and will be evaluated and rewarded at the discretion of the Uniswap Labs team. For critical bugs that lead to any loss of LP funds, rewards of up to $500,000 will be granted. Lower severity bugs will be rewarded at the discretion of the team. In addition, all vulnerabilities disclosed prior to the mainnet launch date will be subject to receive higher rewards. + +## Disclosure + +Any vulnerability or bug discovered must be reported only to the following email: [security@uniswap.org](mailto:security@uniswap.org). + +The vulnerability must not be disclosed publicly or to any other person, entity or email address before Uniswap Labs has been notified, has fixed the issue, and has granted permission for public disclosure. In addition, disclosure must be made within 24 hours following discovery of the vulnerability. + +A detailed report of a vulnerability increases the likelihood of a reward and may increase the reward amount. Please provide as much information about the vulnerability as possible, including: + +- The conditions on which reproducing the bug is contingent. +- The steps needed to reproduce the bug or, preferably, a proof of concept. +- The potential implications of the vulnerability being abused. + +Anyone who reports a unique, previously-unreported vulnerability that results in a change to the code or a configuration change and who keeps such vulnerability confidential until it has been resolved by our engineers will be recognized publicly for their contribution if they so choose. + +## Eligibility + +To be eligible for a reward under this Program, you must: + +- Discover a previously unreported, non-public vulnerability that would result in a loss of and/or lock on any ERC-20 token on Uniswap V3 (but not on any third party platform interacting with Uniswap V3) and that is within the scope of this Program. Vulnerabilities must be distinct from the issues covered in the Trail of Bits or ABDK audits. +- Be the first to disclose the unique vulnerability to [security@uniswap.org](mailto:security@uniswap.org), in compliance with the disclosure requirements above. If similar vulnerabilities are reported within the same 24 hour period, rewards will be split at the discretion of Uniswap Labs. +- Provide sufficient information to enable our engineers to reproduce and fix the vulnerability. +- Not engage in any unlawful conduct when disclosing the bug, including through threats, demands, or any other coercive tactics. +- Not exploit the vulnerability in any way, including through making it public or by obtaining a profit (other than a reward under this Program). +- Make a good faith effort to avoid privacy violations, destruction of data, interruption or degradation of Uniswap V3. +- Submit only one vulnerability per submission, unless you need to chain vulnerabilities to provide impact regarding any of the vulnerabilities. +- Not submit a vulnerability caused by an underlying issue that is the same as an issue on which a reward has been paid under this Program. +- Not be one of our current or former employees, vendors, or contractors or an employee of any of those vendors or contractors. +- Not be subject to US sanctions or reside in a US-embargoed country. +- Be at least 18 years of age or, if younger, submit your vulnerability with the consent of your parent or guardian. + +## Other Terms + +By submitting your report, you grant Uniswap Labs any and all rights, including intellectual property rights, needed to validate, mitigate, and disclose the vulnerability. All reward decisions, including eligibility for and amounts of the rewards and the manner in which such rewards will be paid, are made at our sole discretion. + +The terms and conditions of this Program may be altered at any time. diff --git a/lib/uniswap/v3-core/contracts/NoDelegateCall.sol b/lib/uniswap/v3-core/contracts/NoDelegateCall.sol new file mode 100644 index 0000000..5411979 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/NoDelegateCall.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +/// @title Prevents delegatecall to a contract +/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract +abstract contract NoDelegateCall { + /// @dev The original address of this contract + address private immutable original; + + constructor() { + // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode. + // In other words, this variable won't change when it's checked at runtime. + original = address(this); + } + + /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method, + /// and the use of immutable means the address bytes are copied in every place the modifier is used. + function checkNotDelegateCall() private view { + require(address(this) == original); + } + + /// @notice Prevents delegatecall into the modified method + modifier noDelegateCall() { + checkNotDelegateCall(); + _; + } +} diff --git a/lib/uniswap/v3-core/contracts/UniswapV3Factory.sol b/lib/uniswap/v3-core/contracts/UniswapV3Factory.sol new file mode 100644 index 0000000..3553ce5 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/UniswapV3Factory.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +import './interfaces/IUniswapV3Factory.sol'; + +import './UniswapV3PoolDeployer.sol'; +import './NoDelegateCall.sol'; + +import './UniswapV3Pool.sol'; + +/// @title Canonical Uniswap V3 factory +/// @notice Deploys Uniswap V3 pools and manages ownership and control over pool protocol fees +contract UniswapV3Factory is IUniswapV3Factory, UniswapV3PoolDeployer, NoDelegateCall { + /// @inheritdoc IUniswapV3Factory + address public override owner; + + /// @inheritdoc IUniswapV3Factory + mapping(uint24 => int24) public override feeAmountTickSpacing; + /// @inheritdoc IUniswapV3Factory + mapping(address => mapping(address => mapping(uint24 => address))) public override getPool; + + constructor() { + owner = msg.sender; + emit OwnerChanged(address(0), msg.sender); + + feeAmountTickSpacing[500] = 10; + emit FeeAmountEnabled(500, 10); + feeAmountTickSpacing[3000] = 60; + emit FeeAmountEnabled(3000, 60); + feeAmountTickSpacing[10000] = 200; + emit FeeAmountEnabled(10000, 200); + } + + /// @inheritdoc IUniswapV3Factory + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external override noDelegateCall returns (address pool) { + require(tokenA != tokenB); + (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0)); + int24 tickSpacing = feeAmountTickSpacing[fee]; + require(tickSpacing != 0); + require(getPool[token0][token1][fee] == address(0)); + pool = deploy(address(this), token0, token1, fee, tickSpacing); + getPool[token0][token1][fee] = pool; + // populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses + getPool[token1][token0][fee] = pool; + emit PoolCreated(token0, token1, fee, tickSpacing, pool); + } + + /// @inheritdoc IUniswapV3Factory + function setOwner(address _owner) external override { + require(msg.sender == owner); + emit OwnerChanged(owner, _owner); + owner = _owner; + } + + /// @inheritdoc IUniswapV3Factory + function enableFeeAmount(uint24 fee, int24 tickSpacing) public override { + require(msg.sender == owner); + require(fee < 1000000); + // tick spacing is capped at 16384 to prevent the situation where tickSpacing is so large that + // TickBitmap#nextInitializedTickWithinOneWord overflows int24 container from a valid tick + // 16384 ticks represents a >5x price change with ticks of 1 bips + require(tickSpacing > 0 && tickSpacing < 16384); + require(feeAmountTickSpacing[fee] == 0); + + feeAmountTickSpacing[fee] = tickSpacing; + emit FeeAmountEnabled(fee, tickSpacing); + } +} diff --git a/lib/uniswap/v3-core/contracts/UniswapV3Pool.sol b/lib/uniswap/v3-core/contracts/UniswapV3Pool.sol new file mode 100644 index 0000000..ca7c59f --- /dev/null +++ b/lib/uniswap/v3-core/contracts/UniswapV3Pool.sol @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +import './interfaces/IUniswapV3Pool.sol'; + +import './NoDelegateCall.sol'; + +import './libraries/LowGasSafeMath.sol'; +import './libraries/SafeCast.sol'; +import './libraries/Tick.sol'; +import './libraries/TickBitmap.sol'; +import './libraries/Position.sol'; +import './libraries/Oracle.sol'; + +import './libraries/FullMath.sol'; +import './libraries/FixedPoint128.sol'; +import './libraries/TransferHelper.sol'; +import './libraries/TickMath.sol'; +import './libraries/LiquidityMath.sol'; +import './libraries/SqrtPriceMath.sol'; +import './libraries/SwapMath.sol'; + +import './interfaces/IUniswapV3PoolDeployer.sol'; +import './interfaces/IUniswapV3Factory.sol'; +import './interfaces/IERC20Minimal.sol'; +import './interfaces/callback/IUniswapV3MintCallback.sol'; +import './interfaces/callback/IUniswapV3SwapCallback.sol'; +import './interfaces/callback/IUniswapV3FlashCallback.sol'; + +contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall { + using LowGasSafeMath for uint256; + using LowGasSafeMath for int256; + using SafeCast for uint256; + using SafeCast for int256; + using Tick for mapping(int24 => Tick.Info); + using TickBitmap for mapping(int16 => uint256); + using Position for mapping(bytes32 => Position.Info); + using Position for Position.Info; + using Oracle for Oracle.Observation[65535]; + + /// @inheritdoc IUniswapV3PoolImmutables + address public immutable override factory; + /// @inheritdoc IUniswapV3PoolImmutables + address public immutable override token0; + /// @inheritdoc IUniswapV3PoolImmutables + address public immutable override token1; + /// @inheritdoc IUniswapV3PoolImmutables + uint24 public immutable override fee; + + /// @inheritdoc IUniswapV3PoolImmutables + int24 public immutable override tickSpacing; + + /// @inheritdoc IUniswapV3PoolImmutables + uint128 public immutable override maxLiquidityPerTick; + + struct Slot0 { + // the current price + uint160 sqrtPriceX96; + // the current tick + int24 tick; + // the most-recently updated index of the observations array + uint16 observationIndex; + // the current maximum number of observations that are being stored + uint16 observationCardinality; + // the next maximum number of observations to store, triggered in observations.write + uint16 observationCardinalityNext; + // the current protocol fee as a percentage of the swap fee taken on withdrawal + // represented as an integer denominator (1/x)% + uint8 feeProtocol; + // whether the pool is locked + bool unlocked; + } + /// @inheritdoc IUniswapV3PoolState + Slot0 public override slot0; + + /// @inheritdoc IUniswapV3PoolState + uint256 public override feeGrowthGlobal0X128; + /// @inheritdoc IUniswapV3PoolState + uint256 public override feeGrowthGlobal1X128; + + // accumulated protocol fees in token0/token1 units + struct ProtocolFees { + uint128 token0; + uint128 token1; + } + /// @inheritdoc IUniswapV3PoolState + ProtocolFees public override protocolFees; + + /// @inheritdoc IUniswapV3PoolState + uint128 public override liquidity; + + /// @inheritdoc IUniswapV3PoolState + mapping(int24 => Tick.Info) public override ticks; + /// @inheritdoc IUniswapV3PoolState + mapping(int16 => uint256) public override tickBitmap; + /// @inheritdoc IUniswapV3PoolState + mapping(bytes32 => Position.Info) public override positions; + /// @inheritdoc IUniswapV3PoolState + Oracle.Observation[65535] public override observations; + + /// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance + /// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because + /// we use balance checks to determine the payment status of interactions such as mint, swap and flash. + modifier lock() { + require(slot0.unlocked, 'LOK'); + slot0.unlocked = false; + _; + slot0.unlocked = true; + } + + /// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner() + modifier onlyFactoryOwner() { + require(msg.sender == IUniswapV3Factory(factory).owner()); + _; + } + + constructor() { + int24 _tickSpacing; + (factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters(); + tickSpacing = _tickSpacing; + + maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing); + } + + /// @dev Common checks for valid tick inputs. + function checkTicks(int24 tickLower, int24 tickUpper) private pure { + require(tickLower < tickUpper, 'TLU'); + require(tickLower >= TickMath.MIN_TICK, 'TLM'); + require(tickUpper <= TickMath.MAX_TICK, 'TUM'); + } + + /// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests. + function _blockTimestamp() internal view virtual returns (uint32) { + return uint32(block.timestamp); // truncation is desired + } + + /// @dev Get the pool's balance of token0 + /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize + /// check + function balance0() private view returns (uint256) { + (bool success, bytes memory data) = token0.staticcall( + abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) + ); + require(success && data.length >= 32); + return abi.decode(data, (uint256)); + } + + /// @dev Get the pool's balance of token1 + /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize + /// check + function balance1() private view returns (uint256) { + (bool success, bytes memory data) = token1.staticcall( + abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)) + ); + require(success && data.length >= 32); + return abi.decode(data, (uint256)); + } + + /// @inheritdoc IUniswapV3PoolDerivedState + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + override + noDelegateCall + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint32 secondsInside + ) + { + checkTicks(tickLower, tickUpper); + + int56 tickCumulativeLower; + int56 tickCumulativeUpper; + uint160 secondsPerLiquidityOutsideLowerX128; + uint160 secondsPerLiquidityOutsideUpperX128; + uint32 secondsOutsideLower; + uint32 secondsOutsideUpper; + + { + Tick.Info storage lower = ticks[tickLower]; + Tick.Info storage upper = ticks[tickUpper]; + bool initializedLower; + (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = ( + lower.tickCumulativeOutside, + lower.secondsPerLiquidityOutsideX128, + lower.secondsOutside, + lower.initialized + ); + require(initializedLower); + + bool initializedUpper; + (tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = ( + upper.tickCumulativeOutside, + upper.secondsPerLiquidityOutsideX128, + upper.secondsOutside, + upper.initialized + ); + require(initializedUpper); + } + + Slot0 memory _slot0 = slot0; + + if (_slot0.tick < tickLower) { + return ( + tickCumulativeLower - tickCumulativeUpper, + secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128, + secondsOutsideLower - secondsOutsideUpper + ); + } else if (_slot0.tick < tickUpper) { + uint32 time = _blockTimestamp(); + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( + time, + 0, + _slot0.tick, + _slot0.observationIndex, + liquidity, + _slot0.observationCardinality + ); + return ( + tickCumulative - tickCumulativeLower - tickCumulativeUpper, + secondsPerLiquidityCumulativeX128 - + secondsPerLiquidityOutsideLowerX128 - + secondsPerLiquidityOutsideUpperX128, + time - secondsOutsideLower - secondsOutsideUpper + ); + } else { + return ( + tickCumulativeUpper - tickCumulativeLower, + secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128, + secondsOutsideUpper - secondsOutsideLower + ); + } + } + + /// @inheritdoc IUniswapV3PoolDerivedState + function observe(uint32[] calldata secondsAgos) + external + view + override + noDelegateCall + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) + { + return + observations.observe( + _blockTimestamp(), + secondsAgos, + slot0.tick, + slot0.observationIndex, + liquidity, + slot0.observationCardinality + ); + } + + /// @inheritdoc IUniswapV3PoolActions + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) + external + override + lock + noDelegateCall + { + uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event + uint16 observationCardinalityNextNew = observations.grow( + observationCardinalityNextOld, + observationCardinalityNext + ); + slot0.observationCardinalityNext = observationCardinalityNextNew; + if (observationCardinalityNextOld != observationCardinalityNextNew) + emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew); + } + + /// @inheritdoc IUniswapV3PoolActions + /// @dev not locked because it initializes unlocked + function initialize(uint160 sqrtPriceX96) external override { + require(slot0.sqrtPriceX96 == 0, 'AI'); + + int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96); + + (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp()); + + slot0 = Slot0({ + sqrtPriceX96: sqrtPriceX96, + tick: tick, + observationIndex: 0, + observationCardinality: cardinality, + observationCardinalityNext: cardinalityNext, + feeProtocol: 0, + unlocked: true + }); + + emit Initialize(sqrtPriceX96, tick); + } + + struct ModifyPositionParams { + // the address that owns the position + address owner; + // the lower and upper tick of the position + int24 tickLower; + int24 tickUpper; + // any change in liquidity + int128 liquidityDelta; + } + + /// @dev Effect some changes to a position + /// @param params the position details and the change to the position's liquidity to effect + /// @return position a storage pointer referencing the position with the given owner and tick range + /// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient + /// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient + function _modifyPosition(ModifyPositionParams memory params) + private + noDelegateCall + returns ( + Position.Info storage position, + int256 amount0, + int256 amount1 + ) + { + checkTicks(params.tickLower, params.tickUpper); + + Slot0 memory _slot0 = slot0; // SLOAD for gas optimization + + position = _updatePosition( + params.owner, + params.tickLower, + params.tickUpper, + params.liquidityDelta, + _slot0.tick + ); + + if (params.liquidityDelta != 0) { + if (_slot0.tick < params.tickLower) { + // current tick is below the passed range; liquidity can only become in range by crossing from left to + // right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it + amount0 = SqrtPriceMath.getAmount0Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + } else if (_slot0.tick < params.tickUpper) { + // current tick is inside the passed range + uint128 liquidityBefore = liquidity; // SLOAD for gas optimization + + // write an oracle entry + (slot0.observationIndex, slot0.observationCardinality) = observations.write( + _slot0.observationIndex, + _blockTimestamp(), + _slot0.tick, + liquidityBefore, + _slot0.observationCardinality, + _slot0.observationCardinalityNext + ); + + amount0 = SqrtPriceMath.getAmount0Delta( + _slot0.sqrtPriceX96, + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + amount1 = SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + _slot0.sqrtPriceX96, + params.liquidityDelta + ); + + liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta); + } else { + // current tick is above the passed range; liquidity can only become in range by crossing from right to + // left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it + amount1 = SqrtPriceMath.getAmount1Delta( + TickMath.getSqrtRatioAtTick(params.tickLower), + TickMath.getSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta + ); + } + } + } + + /// @dev Gets and updates a position with the given liquidity delta + /// @param owner the owner of the position + /// @param tickLower the lower tick of the position's tick range + /// @param tickUpper the upper tick of the position's tick range + /// @param tick the current tick, passed to avoid sloads + function _updatePosition( + address owner, + int24 tickLower, + int24 tickUpper, + int128 liquidityDelta, + int24 tick + ) private returns (Position.Info storage position) { + position = positions.get(owner, tickLower, tickUpper); + + uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization + uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization + + // if we need to update the ticks, do it + bool flippedLower; + bool flippedUpper; + if (liquidityDelta != 0) { + uint32 time = _blockTimestamp(); + (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = observations.observeSingle( + time, + 0, + slot0.tick, + slot0.observationIndex, + liquidity, + slot0.observationCardinality + ); + + flippedLower = ticks.update( + tickLower, + tick, + liquidityDelta, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + secondsPerLiquidityCumulativeX128, + tickCumulative, + time, + false, + maxLiquidityPerTick + ); + flippedUpper = ticks.update( + tickUpper, + tick, + liquidityDelta, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + secondsPerLiquidityCumulativeX128, + tickCumulative, + time, + true, + maxLiquidityPerTick + ); + + if (flippedLower) { + tickBitmap.flipTick(tickLower, tickSpacing); + } + if (flippedUpper) { + tickBitmap.flipTick(tickUpper, tickSpacing); + } + } + + (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = ticks.getFeeGrowthInside( + tickLower, + tickUpper, + tick, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128 + ); + + position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128); + + // clear any tick data that is no longer needed + if (liquidityDelta < 0) { + if (flippedLower) { + ticks.clear(tickLower); + } + if (flippedUpper) { + ticks.clear(tickUpper); + } + } + } + + /// @inheritdoc IUniswapV3PoolActions + /// @dev noDelegateCall is applied indirectly via _modifyPosition + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes calldata data + ) external override lock returns (uint256 amount0, uint256 amount1) { + require(amount > 0); + (, int256 amount0Int, int256 amount1Int) = _modifyPosition( + ModifyPositionParams({ + owner: recipient, + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: int256(amount).toInt128() + }) + ); + + amount0 = uint256(amount0Int); + amount1 = uint256(amount1Int); + + uint256 balance0Before; + uint256 balance1Before; + if (amount0 > 0) balance0Before = balance0(); + if (amount1 > 0) balance1Before = balance1(); + IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data); + if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0'); + if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1'); + + emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1); + } + + /// @inheritdoc IUniswapV3PoolActions + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external override lock returns (uint128 amount0, uint128 amount1) { + // we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1} + Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper); + + amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested; + amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested; + + if (amount0 > 0) { + position.tokensOwed0 -= amount0; + TransferHelper.safeTransfer(token0, recipient, amount0); + } + if (amount1 > 0) { + position.tokensOwed1 -= amount1; + TransferHelper.safeTransfer(token1, recipient, amount1); + } + + emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1); + } + + /// @inheritdoc IUniswapV3PoolActions + /// @dev noDelegateCall is applied indirectly via _modifyPosition + function burn( + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external override lock returns (uint256 amount0, uint256 amount1) { + (Position.Info storage position, int256 amount0Int, int256 amount1Int) = _modifyPosition( + ModifyPositionParams({ + owner: msg.sender, + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: -int256(amount).toInt128() + }) + ); + + amount0 = uint256(-amount0Int); + amount1 = uint256(-amount1Int); + + if (amount0 > 0 || amount1 > 0) { + (position.tokensOwed0, position.tokensOwed1) = ( + position.tokensOwed0 + uint128(amount0), + position.tokensOwed1 + uint128(amount1) + ); + } + + emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1); + } + + struct SwapCache { + // the protocol fee for the input token + uint8 feeProtocol; + // liquidity at the beginning of the swap + uint128 liquidityStart; + // the timestamp of the current block + uint32 blockTimestamp; + // the current value of the tick accumulator, computed only if we cross an initialized tick + int56 tickCumulative; + // the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick + uint160 secondsPerLiquidityCumulativeX128; + // whether we've computed and cached the above two accumulators + bool computedLatestObservation; + } + + // the top level state of the swap, the results of which are recorded in storage at the end + struct SwapState { + // the amount remaining to be swapped in/out of the input/output asset + int256 amountSpecifiedRemaining; + // the amount already swapped out/in of the output/input asset + int256 amountCalculated; + // current sqrt(price) + uint160 sqrtPriceX96; + // the tick associated with the current price + int24 tick; + // the global fee growth of the input token + uint256 feeGrowthGlobalX128; + // amount of input token paid as protocol fee + uint128 protocolFee; + // the current liquidity in range + uint128 liquidity; + } + + struct StepComputations { + // the price at the beginning of the step + uint160 sqrtPriceStartX96; + // the next tick to swap to from the current tick in the swap direction + int24 tickNext; + // whether tickNext is initialized or not + bool initialized; + // sqrt(price) for the next tick (1/0) + uint160 sqrtPriceNextX96; + // how much is being swapped in in this step + uint256 amountIn; + // how much is being swapped out + uint256 amountOut; + // how much fee is being paid in + uint256 feeAmount; + } + + /// @inheritdoc IUniswapV3PoolActions + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external override noDelegateCall returns (int256 amount0, int256 amount1) { + require(amountSpecified != 0, 'AS'); + + Slot0 memory slot0Start = slot0; + + require(slot0Start.unlocked, 'LOK'); + require( + zeroForOne + ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO + : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO, + 'SPL' + ); + + slot0.unlocked = false; + + SwapCache memory cache = SwapCache({ + liquidityStart: liquidity, + blockTimestamp: _blockTimestamp(), + feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4), + secondsPerLiquidityCumulativeX128: 0, + tickCumulative: 0, + computedLatestObservation: false + }); + + bool exactInput = amountSpecified > 0; + + SwapState memory state = SwapState({ + amountSpecifiedRemaining: amountSpecified, + amountCalculated: 0, + sqrtPriceX96: slot0Start.sqrtPriceX96, + tick: slot0Start.tick, + feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128, + protocolFee: 0, + liquidity: cache.liquidityStart + }); + + // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit + while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) { + StepComputations memory step; + + step.sqrtPriceStartX96 = state.sqrtPriceX96; + + (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord( + state.tick, + tickSpacing, + zeroForOne + ); + + // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds + if (step.tickNext < TickMath.MIN_TICK) { + step.tickNext = TickMath.MIN_TICK; + } else if (step.tickNext > TickMath.MAX_TICK) { + step.tickNext = TickMath.MAX_TICK; + } + + // get the price for the next tick + step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext); + + // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted + (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep( + state.sqrtPriceX96, + (zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96) + ? sqrtPriceLimitX96 + : step.sqrtPriceNextX96, + state.liquidity, + state.amountSpecifiedRemaining, + fee + ); + + if (exactInput) { + state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256(); + state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256()); + } else { + state.amountSpecifiedRemaining += step.amountOut.toInt256(); + state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256()); + } + + // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee + if (cache.feeProtocol > 0) { + uint256 delta = step.feeAmount / cache.feeProtocol; + step.feeAmount -= delta; + state.protocolFee += uint128(delta); + } + + // update global fee tracker + if (state.liquidity > 0) + state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity); + + // shift tick if we reached the next price + if (state.sqrtPriceX96 == step.sqrtPriceNextX96) { + // if the tick is initialized, run the tick transition + if (step.initialized) { + // check for the placeholder value, which we replace with the actual value the first time the swap + // crosses an initialized tick + if (!cache.computedLatestObservation) { + (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle( + cache.blockTimestamp, + 0, + slot0Start.tick, + slot0Start.observationIndex, + cache.liquidityStart, + slot0Start.observationCardinality + ); + cache.computedLatestObservation = true; + } + int128 liquidityNet = ticks.cross( + step.tickNext, + (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128), + (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128), + cache.secondsPerLiquidityCumulativeX128, + cache.tickCumulative, + cache.blockTimestamp + ); + // if we're moving leftward, we interpret liquidityNet as the opposite sign + // safe because liquidityNet cannot be type(int128).min + if (zeroForOne) liquidityNet = -liquidityNet; + + state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet); + } + + state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext; + } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) { + // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved + state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96); + } + } + + // update tick and write an oracle entry if the tick change + if (state.tick != slot0Start.tick) { + (uint16 observationIndex, uint16 observationCardinality) = observations.write( + slot0Start.observationIndex, + cache.blockTimestamp, + slot0Start.tick, + cache.liquidityStart, + slot0Start.observationCardinality, + slot0Start.observationCardinalityNext + ); + (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = ( + state.sqrtPriceX96, + state.tick, + observationIndex, + observationCardinality + ); + } else { + // otherwise just update the price + slot0.sqrtPriceX96 = state.sqrtPriceX96; + } + + // update liquidity if it changed + if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity; + + // update fee growth global and, if necessary, protocol fees + // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees + if (zeroForOne) { + feeGrowthGlobal0X128 = state.feeGrowthGlobalX128; + if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee; + } else { + feeGrowthGlobal1X128 = state.feeGrowthGlobalX128; + if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee; + } + + (amount0, amount1) = zeroForOne == exactInput + ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated) + : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining); + + // do the transfers and collect payment + if (zeroForOne) { + if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1)); + + uint256 balance0Before = balance0(); + IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); + require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA'); + } else { + if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0)); + + uint256 balance1Before = balance1(); + IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data); + require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA'); + } + + emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick); + slot0.unlocked = true; + } + + /// @inheritdoc IUniswapV3PoolActions + function flash( + address recipient, + uint256 amount0, + uint256 amount1, + bytes calldata data + ) external override lock noDelegateCall { + uint128 _liquidity = liquidity; + require(_liquidity > 0, 'L'); + + uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6); + uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6); + uint256 balance0Before = balance0(); + uint256 balance1Before = balance1(); + + if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0); + if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1); + + IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data); + + uint256 balance0After = balance0(); + uint256 balance1After = balance1(); + + require(balance0Before.add(fee0) <= balance0After, 'F0'); + require(balance1Before.add(fee1) <= balance1After, 'F1'); + + // sub is safe because we know balanceAfter is gt balanceBefore by at least fee + uint256 paid0 = balance0After - balance0Before; + uint256 paid1 = balance1After - balance1Before; + + if (paid0 > 0) { + uint8 feeProtocol0 = slot0.feeProtocol % 16; + uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0; + if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0); + feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity); + } + if (paid1 > 0) { + uint8 feeProtocol1 = slot0.feeProtocol >> 4; + uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1; + if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1); + feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity); + } + + emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1); + } + + /// @inheritdoc IUniswapV3PoolOwnerActions + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner { + require( + (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && + (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)) + ); + uint8 feeProtocolOld = slot0.feeProtocol; + slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4); + emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1); + } + + /// @inheritdoc IUniswapV3PoolOwnerActions + function collectProtocol( + address recipient, + uint128 amount0Requested, + uint128 amount1Requested + ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) { + amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested; + amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested; + + if (amount0 > 0) { + if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings + protocolFees.token0 -= amount0; + TransferHelper.safeTransfer(token0, recipient, amount0); + } + if (amount1 > 0) { + if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings + protocolFees.token1 -= amount1; + TransferHelper.safeTransfer(token1, recipient, amount1); + } + + emit CollectProtocol(msg.sender, recipient, amount0, amount1); + } +} diff --git a/lib/uniswap/v3-core/contracts/UniswapV3PoolDeployer.sol b/lib/uniswap/v3-core/contracts/UniswapV3PoolDeployer.sol new file mode 100644 index 0000000..02bfd53 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/UniswapV3PoolDeployer.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.7.6; + +import './interfaces/IUniswapV3PoolDeployer.sol'; + +import './UniswapV3Pool.sol'; + +contract UniswapV3PoolDeployer is IUniswapV3PoolDeployer { + struct Parameters { + address factory; + address token0; + address token1; + uint24 fee; + int24 tickSpacing; + } + + /// @inheritdoc IUniswapV3PoolDeployer + Parameters public override parameters; + + /// @dev Deploys a pool with the given parameters by transiently setting the parameters storage slot and then + /// clearing it after deploying the pool. + /// @param factory The contract address of the Uniswap V3 factory + /// @param token0 The first token of the pool by address sort order + /// @param token1 The second token of the pool by address sort order + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @param tickSpacing The spacing between usable ticks + function deploy( + address factory, + address token0, + address token1, + uint24 fee, + int24 tickSpacing + ) internal returns (address pool) { + parameters = Parameters({factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing}); + pool = address(new UniswapV3Pool{salt: keccak256(abi.encode(token0, token1, fee))}()); + delete parameters; + } +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/IERC20Minimal.sol b/lib/uniswap/v3-core/contracts/interfaces/IERC20Minimal.sol new file mode 100644 index 0000000..c303265 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/IERC20Minimal.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Minimal ERC20 interface for Uniswap +/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 +interface IERC20Minimal { + /// @notice Returns the balance of a token + /// @param account The account for which to look up the number of tokens it has, i.e. its balance + /// @return The number of tokens held by the account + function balanceOf(address account) external view returns (uint256); + + /// @notice Transfers the amount of token from the `msg.sender` to the recipient + /// @param recipient The account that will receive the amount transferred + /// @param amount The number of tokens to send from the sender to the recipient + /// @return Returns true for a successful transfer, false for an unsuccessful transfer + function transfer(address recipient, uint256 amount) external returns (bool); + + /// @notice Returns the current allowance given to a spender by an owner + /// @param owner The account of the token owner + /// @param spender The account of the token spender + /// @return The current allowance granted by `owner` to `spender` + function allowance(address owner, address spender) external view returns (uint256); + + /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` + /// @param spender The account which will be allowed to spend a given amount of the owners tokens + /// @param amount The amount of tokens allowed to be used by `spender` + /// @return Returns true for a successful approval, false for unsuccessful + function approve(address spender, uint256 amount) external returns (bool); + + /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` + /// @param sender The account from which the transfer will be initiated + /// @param recipient The recipient of the transfer + /// @param amount The amount of the transfer + /// @return Returns true for a successful transfer, false for unsuccessful + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. + /// @param from The account from which the tokens were sent, i.e. the balance decreased + /// @param to The account to which the tokens were sent, i.e. the balance increased + /// @param value The amount of tokens that were transferred + event Transfer(address indexed from, address indexed to, uint256 value); + + /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. + /// @param owner The account that approved spending of its tokens + /// @param spender The account for which the spending allowance was modified + /// @param value The new allowance from the owner to the spender + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol b/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol new file mode 100644 index 0000000..540cfdc --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title The interface for the Uniswap V3 Factory +/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees +interface IUniswapV3Factory { + /// @notice Emitted when the owner of the factory is changed + /// @param oldOwner The owner before the owner was changed + /// @param newOwner The owner after the owner was changed + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + + /// @notice Emitted when a pool is created + /// @param token0 The first token of the pool by address sort order + /// @param token1 The second token of the pool by address sort order + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks + /// @param pool The address of the created pool + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + int24 tickSpacing, + address pool + ); + + /// @notice Emitted when a new fee amount is enabled for pool creation via the factory + /// @param fee The enabled fee, denominated in hundredths of a bip + /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee + event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); + + /// @notice Returns the current owner of the factory + /// @dev Can be changed by the current owner via setOwner + /// @return The address of the factory owner + function owner() external view returns (address); + + /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled + /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context + /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee + /// @return The tick spacing + function feeAmountTickSpacing(uint24 fee) external view returns (int24); + + /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist + /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @return pool The pool address + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) external view returns (address pool); + + /// @notice Creates a pool for the given two tokens and fee + /// @param tokenA One of the two tokens in the desired pool + /// @param tokenB The other of the two tokens in the desired pool + /// @param fee The desired fee for the pool + /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved + /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments + /// are invalid. + /// @return pool The address of the newly created pool + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external returns (address pool); + + /// @notice Updates the owner of the factory + /// @dev Must be called by the current owner + /// @param _owner The new owner of the factory + function setOwner(address _owner) external; + + /// @notice Enables a fee amount with the given tickSpacing + /// @dev Fee amounts may never be removed once enabled + /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) + /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount + function enableFeeAmount(uint24 fee, int24 tickSpacing) external; +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol b/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol new file mode 100644 index 0000000..56df050 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import './pool/IUniswapV3PoolImmutables.sol'; +import './pool/IUniswapV3PoolState.sol'; +import './pool/IUniswapV3PoolDerivedState.sol'; +import './pool/IUniswapV3PoolActions.sol'; +import './pool/IUniswapV3PoolOwnerActions.sol'; +import './pool/IUniswapV3PoolEvents.sol'; + +/// @title The interface for a Uniswap V3 Pool +/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform +/// to the ERC20 specification +/// @dev The pool interface is broken up into many smaller pieces +interface IUniswapV3Pool is + IUniswapV3PoolImmutables, + IUniswapV3PoolState, + IUniswapV3PoolDerivedState, + IUniswapV3PoolActions, + IUniswapV3PoolOwnerActions, + IUniswapV3PoolEvents +{ + +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3PoolDeployer.sol b/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3PoolDeployer.sol new file mode 100644 index 0000000..72096c1 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/IUniswapV3PoolDeployer.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools +/// @notice A contract that constructs a pool must implement this to pass arguments to the pool +/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash +/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain +interface IUniswapV3PoolDeployer { + /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation. + /// @dev Called by the pool constructor to fetch the parameters of the pool + /// Returns factory The factory address + /// Returns token0 The first token of the pool by address sort order + /// Returns token1 The second token of the pool by address sort order + /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// Returns tickSpacing The minimum number of ticks between initialized ticks + function parameters() + external + view + returns ( + address factory, + address token0, + address token1, + uint24 fee, + int24 tickSpacing + ); +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/LICENSE b/lib/uniswap/v3-core/contracts/interfaces/LICENSE new file mode 100644 index 0000000..7f6aca7 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/LICENSE @@ -0,0 +1,445 @@ +This software is available under your choice of the GNU General Public +License, version 2 or later, or the Business Source License, as set +forth below. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. + + +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Uniswap Labs + +Licensed Work: Uniswap V3 Core + The Licensed Work is (c) 2021 Uniswap Labs + +Additional Use Grant: Any uses listed and defined at + v3-core-license-grants.uniswap.eth + +Change Date: The earlier of 2023-04-01 or a date specified at + v3-core-license-date.uniswap.eth + +Change License: GNU General Public License v2.0 or later + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol b/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol new file mode 100644 index 0000000..18e54c4 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#flash +/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface +interface IUniswapV3FlashCallback { + /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash. + /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param fee0 The fee amount in token0 due to the pool by the end of the flash + /// @param fee1 The fee amount in token1 due to the pool by the end of the flash + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call + function uniswapV3FlashCallback( + uint256 fee0, + uint256 fee1, + bytes calldata data + ) external; +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol b/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol new file mode 100644 index 0000000..85447e8 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#mint +/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface +interface IUniswapV3MintCallback { + /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint. + /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity + /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call + function uniswapV3MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external; +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol b/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol new file mode 100644 index 0000000..9f183b2 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Callback for IUniswapV3PoolActions#swap +/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface +interface IUniswapV3SwapCallback { + /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. + /// @dev In the implementation you must pay the pool tokens owed for the swap. + /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. + /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. + /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. + /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by + /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. + /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol new file mode 100644 index 0000000..44fb61c --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Permissionless pool actions +/// @notice Contains pool methods that can be called by anyone +interface IUniswapV3PoolActions { + /// @notice Sets the initial price for the pool + /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value + /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 + function initialize(uint160 sqrtPriceX96) external; + + /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position + /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback + /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends + /// on tickLower, tickUpper, the amount of liquidity, and the current price. + /// @param recipient The address for which the liquidity will be created + /// @param tickLower The lower tick of the position in which to add liquidity + /// @param tickUpper The upper tick of the position in which to add liquidity + /// @param amount The amount of liquidity to mint + /// @param data Any data that should be passed through to the callback + /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback + /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes calldata data + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Collects tokens owed to a position + /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. + /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or + /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the + /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. + /// @param recipient The address which should receive the fees collected + /// @param tickLower The lower tick of the position for which to collect fees + /// @param tickUpper The upper tick of the position for which to collect fees + /// @param amount0Requested How much token0 should be withdrawn from the fees owed + /// @param amount1Requested How much token1 should be withdrawn from the fees owed + /// @return amount0 The amount of fees collected in token0 + /// @return amount1 The amount of fees collected in token1 + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + + /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position + /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 + /// @dev Fees must be collected separately via a call to #collect + /// @param tickLower The lower tick of the position for which to burn liquidity + /// @param tickUpper The upper tick of the position for which to burn liquidity + /// @param amount How much liquidity to burn + /// @return amount0 The amount of token0 sent to the recipient + /// @return amount1 The amount of token1 sent to the recipient + function burn( + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external returns (uint256 amount0, uint256 amount1); + + /// @notice Swap token0 for token1, or token1 for token0 + /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback + /// @param recipient The address to receive the output of the swap + /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 + /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) + /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this + /// value after the swap. If one for zero, the price cannot be greater than this value after the swap + /// @param data Any data to be passed through to the callback + /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive + /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); + + /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback + /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback + /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling + /// with 0 amount{0,1} and sending the donation amount(s) from the callback + /// @param recipient The address which will receive the token0 and token1 amounts + /// @param amount0 The amount of token0 to send + /// @param amount1 The amount of token1 to send + /// @param data Any data to be passed through to the callback + function flash( + address recipient, + uint256 amount0, + uint256 amount1, + bytes calldata data + ) external; + + /// @notice Increase the maximum number of price and liquidity observations that this pool will store + /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to + /// the input observationCardinalityNext. + /// @param observationCardinalityNext The desired minimum number of observations for the pool to store + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol new file mode 100644 index 0000000..eda3a00 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that is not stored +/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the +/// blockchain. The functions here may have variable gas costs. +interface IUniswapV3PoolDerivedState { + /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp + /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing + /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, + /// you must call it with secondsAgos = [3600, 0]. + /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in + /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. + /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned + /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp + /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block + /// timestamp + function observe(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); + + /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range + /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. + /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first + /// snapshot is taken and the second snapshot is taken. + /// @param tickLower The lower tick of the range + /// @param tickUpper The upper tick of the range + /// @return tickCumulativeInside The snapshot of the tick accumulator for the range + /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range + /// @return secondsInside The snapshot of seconds per liquidity for the range + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint32 secondsInside + ); +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol new file mode 100644 index 0000000..9d915dd --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Events emitted by a pool +/// @notice Contains all events emitted by the pool +interface IUniswapV3PoolEvents { + /// @notice Emitted exactly once by a pool when #initialize is first called on the pool + /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize + /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 + /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool + event Initialize(uint160 sqrtPriceX96, int24 tick); + + /// @notice Emitted when liquidity is minted for a given position + /// @param sender The address that minted the liquidity + /// @param owner The owner of the position and recipient of any minted liquidity + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity minted to the position range + /// @param amount0 How much token0 was required for the minted liquidity + /// @param amount1 How much token1 was required for the minted liquidity + event Mint( + address sender, + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted when fees are collected by the owner of a position + /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees + /// @param owner The owner of the position for which fees are collected + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount0 The amount of token0 fees collected + /// @param amount1 The amount of token1 fees collected + event Collect( + address indexed owner, + address recipient, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount0, + uint128 amount1 + ); + + /// @notice Emitted when a position's liquidity is removed + /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect + /// @param owner The owner of the position for which liquidity is removed + /// @param tickLower The lower tick of the position + /// @param tickUpper The upper tick of the position + /// @param amount The amount of liquidity to remove + /// @param amount0 The amount of token0 withdrawn + /// @param amount1 The amount of token1 withdrawn + event Burn( + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + /// @notice Emitted by the pool for any swaps between token0 and token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the output of the swap + /// @param amount0 The delta of the token0 balance of the pool + /// @param amount1 The delta of the token1 balance of the pool + /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 + /// @param liquidity The liquidity of the pool after the swap + /// @param tick The log base 1.0001 of price of the pool after the swap + event Swap( + address indexed sender, + address indexed recipient, + int256 amount0, + int256 amount1, + uint160 sqrtPriceX96, + uint128 liquidity, + int24 tick + ); + + /// @notice Emitted by the pool for any flashes of token0/token1 + /// @param sender The address that initiated the swap call, and that received the callback + /// @param recipient The address that received the tokens from flash + /// @param amount0 The amount of token0 that was flashed + /// @param amount1 The amount of token1 that was flashed + /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee + /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee + event Flash( + address indexed sender, + address indexed recipient, + uint256 amount0, + uint256 amount1, + uint256 paid0, + uint256 paid1 + ); + + /// @notice Emitted by the pool for increases to the number of observations that can be stored + /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index + /// just before a mint/swap/burn. + /// @param observationCardinalityNextOld The previous value of the next observation cardinality + /// @param observationCardinalityNextNew The updated value of the next observation cardinality + event IncreaseObservationCardinalityNext( + uint16 observationCardinalityNextOld, + uint16 observationCardinalityNextNew + ); + + /// @notice Emitted when the protocol fee is changed by the pool + /// @param feeProtocol0Old The previous value of the token0 protocol fee + /// @param feeProtocol1Old The previous value of the token1 protocol fee + /// @param feeProtocol0New The updated value of the token0 protocol fee + /// @param feeProtocol1New The updated value of the token1 protocol fee + event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); + + /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner + /// @param sender The address that collects the protocol fees + /// @param recipient The address that receives the collected protocol fees + /// @param amount0 The amount of token0 protocol fees that is withdrawn + /// @param amount0 The amount of token1 protocol fees that is withdrawn + event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol new file mode 100644 index 0000000..c9beb15 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that never changes +/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values +interface IUniswapV3PoolImmutables { + /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface + /// @return The contract address + function factory() external view returns (address); + + /// @notice The first of the two tokens of the pool, sorted by address + /// @return The token contract address + function token0() external view returns (address); + + /// @notice The second of the two tokens of the pool, sorted by address + /// @return The token contract address + function token1() external view returns (address); + + /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 + /// @return The fee + function fee() external view returns (uint24); + + /// @notice The pool tick spacing + /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive + /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... + /// This value is an int24 to avoid casting even though it is always positive. + /// @return The tick spacing + function tickSpacing() external view returns (int24); + + /// @notice The maximum amount of position liquidity that can use any tick in the range + /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and + /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool + /// @return The max amount of liquidity per tick + function maxLiquidityPerTick() external view returns (uint128); +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol new file mode 100644 index 0000000..2395ed3 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Permissioned pool actions +/// @notice Contains pool methods that may only be called by the factory owner +interface IUniswapV3PoolOwnerActions { + /// @notice Set the denominator of the protocol's % share of the fees + /// @param feeProtocol0 new protocol fee for token0 of the pool + /// @param feeProtocol1 new protocol fee for token1 of the pool + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; + + /// @notice Collect the protocol fee accrued to the pool + /// @param recipient The address to which collected protocol fees should be sent + /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 + /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 + /// @return amount0 The protocol fee collected in token0 + /// @return amount1 The protocol fee collected in token1 + function collectProtocol( + address recipient, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); +} diff --git a/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol new file mode 100644 index 0000000..620256c --- /dev/null +++ b/lib/uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Pool state that can change +/// @notice These methods compose the pool's state, and can change with any frequency including multiple times +/// per transaction +interface IUniswapV3PoolState { + /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas + /// when accessed externally. + /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value + /// tick The current tick of the pool, i.e. according to the last tick transition that was run. + /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick + /// boundary. + /// observationIndex The index of the last oracle observation that was written, + /// observationCardinality The current maximum number of observations stored in the pool, + /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. + /// feeProtocol The protocol fee for both tokens of the pool. + /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 + /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. + /// unlocked Whether the pool is currently locked to reentrancy + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + + /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal0X128() external view returns (uint256); + + /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool + /// @dev This value can overflow the uint256 + function feeGrowthGlobal1X128() external view returns (uint256); + + /// @notice The amounts of token0 and token1 that are owed to the protocol + /// @dev Protocol fees will never exceed uint128 max in either token + function protocolFees() external view returns (uint128 token0, uint128 token1); + + /// @notice The currently in range liquidity available to the pool + /// @dev This value has no relationship to the total liquidity across all ticks + function liquidity() external view returns (uint128); + + /// @notice Look up information about a specific tick in the pool + /// @param tick The tick to look up + /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or + /// tick upper, + /// liquidityNet how much liquidity changes when the pool price crosses the tick, + /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, + /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, + /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick + /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, + /// secondsOutside the seconds spent on the other side of the tick from the current tick, + /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. + /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. + /// In addition, these values are only relative and must be used only in comparison to previous snapshots for + /// a specific position. + function ticks(int24 tick) + external + view + returns ( + uint128 liquidityGross, + int128 liquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ); + + /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information + function tickBitmap(int16 wordPosition) external view returns (uint256); + + /// @notice Returns the information about a position by the position's key + /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper + /// @return _liquidity The amount of liquidity in the position, + /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, + /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, + /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, + /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke + function positions(bytes32 key) + external + view + returns ( + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + + /// @notice Returns data about a specific observation index + /// @param index The element of the observations array to fetch + /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time + /// ago, rather than at a specific index in the array. + /// @return blockTimestamp The timestamp of the observation, + /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, + /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, + /// Returns initialized whether the observation has been initialized and the values are safe to use + function observations(uint256 index) + external + view + returns ( + uint32 blockTimestamp, + int56 tickCumulative, + uint160 secondsPerLiquidityCumulativeX128, + bool initialized + ); +} diff --git a/lib/uniswap/v3-core/contracts/libraries/BitMath.sol b/lib/uniswap/v3-core/contracts/libraries/BitMath.sol new file mode 100644 index 0000000..3a7216c --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/BitMath.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title BitMath +/// @dev This library provides functionality for computing bit properties of an unsigned integer +library BitMath { + /// @notice Returns the index of the most significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) + /// @param x the value for which to compute the most significant bit, must be greater than 0 + /// @return r the index of the most significant bit + function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + if (x >= 0x100000000000000000000000000000000) { + x >>= 128; + r += 128; + } + if (x >= 0x10000000000000000) { + x >>= 64; + r += 64; + } + if (x >= 0x100000000) { + x >>= 32; + r += 32; + } + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 0x4) { + x >>= 2; + r += 2; + } + if (x >= 0x2) r += 1; + } + + /// @notice Returns the index of the least significant bit of the number, + /// where the least significant bit is at index 0 and the most significant bit is at index 255 + /// @dev The function satisfies the property: + /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) + /// @param x the value for which to compute the least significant bit, must be greater than 0 + /// @return r the index of the least significant bit + function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { + require(x > 0); + + r = 255; + if (x & type(uint128).max > 0) { + r -= 128; + } else { + x >>= 128; + } + if (x & type(uint64).max > 0) { + r -= 64; + } else { + x >>= 64; + } + if (x & type(uint32).max > 0) { + r -= 32; + } else { + x >>= 32; + } + if (x & type(uint16).max > 0) { + r -= 16; + } else { + x >>= 16; + } + if (x & type(uint8).max > 0) { + r -= 8; + } else { + x >>= 8; + } + if (x & 0xf > 0) { + r -= 4; + } else { + x >>= 4; + } + if (x & 0x3 > 0) { + r -= 2; + } else { + x >>= 2; + } + if (x & 0x1 > 0) r -= 1; + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/FixedPoint128.sol b/lib/uniswap/v3-core/contracts/libraries/FixedPoint128.sol new file mode 100644 index 0000000..6d6948b --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/FixedPoint128.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.4.0; + +/// @title FixedPoint128 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +library FixedPoint128 { + uint256 internal constant Q128 = 0x100000000000000000000000000000000; +} diff --git a/lib/uniswap/v3-core/contracts/libraries/FixedPoint96.sol b/lib/uniswap/v3-core/contracts/libraries/FixedPoint96.sol new file mode 100644 index 0000000..63b42c2 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/FixedPoint96.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.4.0; + +/// @title FixedPoint96 +/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format) +/// @dev Used in SqrtPriceMath.sol +library FixedPoint96 { + uint8 internal constant RESOLUTION = 96; + uint256 internal constant Q96 = 0x1000000000000000000000000; +} diff --git a/lib/uniswap/v3-core/contracts/libraries/FullMath.sol b/lib/uniswap/v3-core/contracts/libraries/FullMath.sol new file mode 100644 index 0000000..9985059 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/FullMath.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.0 <0.8.0; + +/// @title Contains 512-bit math functions +/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision +/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits +library FullMath { + /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv + function mulDiv( + uint256 a, + uint256 b, + uint256 denominator + ) internal pure returns (uint256 result) { + // 512-bit multiply [prod1 prod0] = a * b + // Compute the product mod 2**256 and mod 2**256 - 1 + // then use the Chinese Remainder Theorem to reconstruct + // the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2**256 + prod0 + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(a, b, not(0)) + prod0 := mul(a, b) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division + if (prod1 == 0) { + require(denominator > 0); + assembly { + result := div(prod0, denominator) + } + return result; + } + + // Make sure the result is less than 2**256. + // Also prevents denominator == 0 + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0] + // Compute remainder using mulmod + uint256 remainder; + assembly { + remainder := mulmod(a, b, denominator) + } + // Subtract 256 bit number from 512 bit number + assembly { + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator + // Compute largest power of two divisor of denominator. + // Always >= 1. + uint256 twos = -denominator & denominator; + // Divide denominator by power of two + assembly { + denominator := div(denominator, twos) + } + + // Divide [prod1 prod0] by the factors of two + assembly { + prod0 := div(prod0, twos) + } + // Shift in bits from prod1 into prod0. For this we need + // to flip `twos` such that it is 2**256 / twos. + // If twos is zero, then it becomes one + assembly { + twos := add(div(sub(0, twos), twos), 1) + } + prod0 |= prod1 * twos; + + // Invert denominator mod 2**256 + // Now that denominator is an odd number, it has an inverse + // modulo 2**256 such that denominator * inv = 1 mod 2**256. + // Compute the inverse by starting with a seed that is correct + // correct for four bits. That is, denominator * inv = 1 mod 2**4 + uint256 inv = (3 * denominator) ^ 2; + // Now use Newton-Raphson iteration to improve the precision. + // Thanks to Hensel's lifting lemma, this also works in modular + // arithmetic, doubling the correct bits in each step. + inv *= 2 - denominator * inv; // inverse mod 2**8 + inv *= 2 - denominator * inv; // inverse mod 2**16 + inv *= 2 - denominator * inv; // inverse mod 2**32 + inv *= 2 - denominator * inv; // inverse mod 2**64 + inv *= 2 - denominator * inv; // inverse mod 2**128 + inv *= 2 - denominator * inv; // inverse mod 2**256 + + // Because the division is now exact we can divide by multiplying + // with the modular inverse of denominator. This will give us the + // correct result modulo 2**256. Since the precoditions guarantee + // that the outcome is less than 2**256, this is the final result. + // We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inv; + return result; + } + + /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 + /// @param a The multiplicand + /// @param b The multiplier + /// @param denominator The divisor + /// @return result The 256-bit result + function mulDivRoundingUp( + uint256 a, + uint256 b, + uint256 denominator + ) internal pure returns (uint256 result) { + result = mulDiv(a, b, denominator); + if (mulmod(a, b, denominator) > 0) { + require(result < type(uint256).max); + result++; + } + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/LICENSE b/lib/uniswap/v3-core/contracts/libraries/LICENSE new file mode 100644 index 0000000..7f6aca7 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/LICENSE @@ -0,0 +1,445 @@ +This software is available under your choice of the GNU General Public +License, version 2 or later, or the Business Source License, as set +forth below. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. + + +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: Uniswap Labs + +Licensed Work: Uniswap V3 Core + The Licensed Work is (c) 2021 Uniswap Labs + +Additional Use Grant: Any uses listed and defined at + v3-core-license-grants.uniswap.eth + +Change Date: The earlier of 2023-04-01 or a date specified at + v3-core-license-date.uniswap.eth + +Change License: GNU General Public License v2.0 or later + +----------------------------------------------------------------------------- + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +----------------------------------------------------------------------------- + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. + +----------------------------------------------------------------------------- + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. diff --git a/lib/uniswap/v3-core/contracts/libraries/LICENSE_MIT b/lib/uniswap/v3-core/contracts/libraries/LICENSE_MIT new file mode 100644 index 0000000..bf4f90a --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/LICENSE_MIT @@ -0,0 +1,20 @@ +Copyright (c) 2021 Remco Bloemen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/lib/uniswap/v3-core/contracts/libraries/LiquidityMath.sol b/lib/uniswap/v3-core/contracts/libraries/LiquidityMath.sol new file mode 100644 index 0000000..d5e2303 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/LiquidityMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math library for liquidity +library LiquidityMath { + /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows + /// @param x The liquidity before change + /// @param y The delta by which liquidity should be changed + /// @return z The liquidity delta + function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) { + if (y < 0) { + require((z = x - uint128(-y)) < x, 'LS'); + } else { + require((z = x + uint128(y)) >= x, 'LA'); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol b/lib/uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol new file mode 100644 index 0000000..dbc817c --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.0; + +/// @title Optimized overflow and underflow safe math operations +/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost +library LowGasSafeMath { + /// @notice Returns x + y, reverts if sum overflows uint256 + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x + y) >= x); + } + + /// @notice Returns x - y, reverts if underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x - y) <= x); + } + + /// @notice Returns x * y, reverts if overflows + /// @param x The multiplicand + /// @param y The multiplier + /// @return z The product of x and y + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + require(x == 0 || (z = x * y) / x == y); + } + + /// @notice Returns x + y, reverts if overflows or underflows + /// @param x The augend + /// @param y The addend + /// @return z The sum of x and y + function add(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x + y) >= x == (y >= 0)); + } + + /// @notice Returns x - y, reverts if overflows or underflows + /// @param x The minuend + /// @param y The subtrahend + /// @return z The difference of x and y + function sub(int256 x, int256 y) internal pure returns (int256 z) { + require((z = x - y) <= x == (y >= 0)); + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/Oracle.sol b/lib/uniswap/v3-core/contracts/libraries/Oracle.sol new file mode 100644 index 0000000..0567165 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/Oracle.sol @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0 <0.8.0; + +/// @title Oracle +/// @notice Provides price and liquidity data useful for a wide variety of system designs +/// @dev Instances of stored oracle data, "observations", are collected in the oracle array +/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the +/// maximum length of the oracle array. New slots will be added when the array is fully populated. +/// Observations are overwritten when the full length of the oracle array is populated. +/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe() +library Oracle { + struct Observation { + // the block timestamp of the observation + uint32 blockTimestamp; + // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized + int56 tickCumulative; + // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized + uint160 secondsPerLiquidityCumulativeX128; + // whether or not the observation is initialized + bool initialized; + } + + /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values + /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows + /// @param last The specified observation to be transformed + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @return Observation The newly populated observation + function transform( + Observation memory last, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity + ) private pure returns (Observation memory) { + uint32 delta = blockTimestamp - last.blockTimestamp; + return + Observation({ + blockTimestamp: blockTimestamp, + tickCumulative: last.tickCumulative + int56(tick) * delta, + secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 + + ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)), + initialized: true + }); + } + + /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array + /// @param self The stored oracle array + /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32 + /// @return cardinality The number of populated elements in the oracle array + /// @return cardinalityNext The new length of the oracle array, independent of population + function initialize(Observation[65535] storage self, uint32 time) + internal + returns (uint16 cardinality, uint16 cardinalityNext) + { + self[0] = Observation({ + blockTimestamp: time, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: 0, + initialized: true + }); + return (1, 1); + } + + /// @notice Writes an oracle observation to the array + /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally. + /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality + /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering. + /// @param self The stored oracle array + /// @param index The index of the observation that was most recently written to the observations array + /// @param blockTimestamp The timestamp of the new observation + /// @param tick The active tick at the time of the new observation + /// @param liquidity The total in-range liquidity at the time of the new observation + /// @param cardinality The number of populated elements in the oracle array + /// @param cardinalityNext The new length of the oracle array, independent of population + /// @return indexUpdated The new index of the most recently written element in the oracle array + /// @return cardinalityUpdated The new cardinality of the oracle array + function write( + Observation[65535] storage self, + uint16 index, + uint32 blockTimestamp, + int24 tick, + uint128 liquidity, + uint16 cardinality, + uint16 cardinalityNext + ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) { + Observation memory last = self[index]; + + // early return if we've already written an observation this block + if (last.blockTimestamp == blockTimestamp) return (index, cardinality); + + // if the conditions are right, we can bump the cardinality + if (cardinalityNext > cardinality && index == (cardinality - 1)) { + cardinalityUpdated = cardinalityNext; + } else { + cardinalityUpdated = cardinality; + } + + indexUpdated = (index + 1) % cardinalityUpdated; + self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity); + } + + /// @notice Prepares the oracle array to store up to `next` observations + /// @param self The stored oracle array + /// @param current The current next cardinality of the oracle array + /// @param next The proposed next cardinality which will be populated in the oracle array + /// @return next The next cardinality which will be populated in the oracle array + function grow( + Observation[65535] storage self, + uint16 current, + uint16 next + ) internal returns (uint16) { + require(current > 0, 'I'); + // no-op if the passed next value isn't greater than the current next value + if (next <= current) return current; + // store in each slot to prevent fresh SSTOREs in swaps + // this data will not be used because the initialized boolean is still false + for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1; + return next; + } + + /// @notice comparator for 32-bit timestamps + /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time + /// @param time A timestamp truncated to 32 bits + /// @param a A comparison timestamp from which to determine the relative position of `time` + /// @param b From which to determine the relative position of `time` + /// @return bool Whether `a` is chronologically <= `b` + function lte( + uint32 time, + uint32 a, + uint32 b + ) private pure returns (bool) { + // if there hasn't been overflow, no need to adjust + if (a <= time && b <= time) return a <= b; + + uint256 aAdjusted = a > time ? a : a + 2**32; + uint256 bAdjusted = b > time ? b : b + 2**32; + + return aAdjusted <= bAdjusted; + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied. + /// The result may be the same observation, or adjacent observations. + /// @dev The answer must be contained in the array, used when the target is located within the stored observation + /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param index The index of the observation that was most recently written to the observations array + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation recorded before, or at, the target + /// @return atOrAfter The observation recorded at, or after, the target + function binarySearch( + Observation[65535] storage self, + uint32 time, + uint32 target, + uint16 index, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + uint256 l = (index + 1) % cardinality; // oldest observation + uint256 r = l + cardinality - 1; // newest observation + uint256 i; + while (true) { + i = (l + r) / 2; + + beforeOrAt = self[i % cardinality]; + + // we've landed on an uninitialized tick, keep searching higher (more recently) + if (!beforeOrAt.initialized) { + l = i + 1; + continue; + } + + atOrAfter = self[(i + 1) % cardinality]; + + bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target); + + // check if we've found the answer! + if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break; + + if (!targetAtOrAfter) r = i - 1; + else l = i + 1; + } + } + + /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied + /// @dev Assumes there is at least 1 initialized observation. + /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp. + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param target The timestamp at which the reserved observation should be for + /// @param tick The active tick at the time of the returned or simulated observation + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The total pool liquidity at the time of the call + /// @param cardinality The number of populated elements in the oracle array + /// @return beforeOrAt The observation which occurred at, or before, the given timestamp + /// @return atOrAfter The observation which occurred at, or after, the given timestamp + function getSurroundingObservations( + Observation[65535] storage self, + uint32 time, + uint32 target, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) { + // optimistically set before to the newest observation + beforeOrAt = self[index]; + + // if the target is chronologically at or after the newest observation, we can early return + if (lte(time, beforeOrAt.blockTimestamp, target)) { + if (beforeOrAt.blockTimestamp == target) { + // if newest observation equals target, we're in the same block, so we can ignore atOrAfter + return (beforeOrAt, atOrAfter); + } else { + // otherwise, we need to transform + return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity)); + } + } + + // now, set before to the oldest observation + beforeOrAt = self[(index + 1) % cardinality]; + if (!beforeOrAt.initialized) beforeOrAt = self[0]; + + // ensure that the target is chronologically at or after the oldest observation + require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD'); + + // if we've reached this point, we have to binary search + return binarySearch(self, time, target, index, cardinality); + } + + /// @dev Reverts if an observation at or before the desired observation timestamp does not exist. + /// 0 may be passed as `secondsAgo' to return the current cumulative values. + /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values + /// at exactly the timestamp between the two observations. + /// @param self The stored oracle array + /// @param time The current block timestamp + /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo` + function observeSingle( + Observation[65535] storage self, + uint32 time, + uint32 secondsAgo, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) { + if (secondsAgo == 0) { + Observation memory last = self[index]; + if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity); + return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128); + } + + uint32 target = time - secondsAgo; + + (Observation memory beforeOrAt, Observation memory atOrAfter) = getSurroundingObservations( + self, + time, + target, + tick, + index, + liquidity, + cardinality + ); + + if (target == beforeOrAt.blockTimestamp) { + // we're at the left boundary + return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128); + } else if (target == atOrAfter.blockTimestamp) { + // we're at the right boundary + return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128); + } else { + // we're in the middle + uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp; + uint32 targetDelta = target - beforeOrAt.blockTimestamp; + return ( + beforeOrAt.tickCumulative + + ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * + targetDelta, + beforeOrAt.secondsPerLiquidityCumulativeX128 + + uint160( + (uint256( + atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128 + ) * targetDelta) / observationTimeDelta + ) + ); + } + } + + /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos` + /// @dev Reverts if `secondsAgos` > oldest observation + /// @param self The stored oracle array + /// @param time The current block.timestamp + /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation + /// @param tick The current tick + /// @param index The index of the observation that was most recently written to the observations array + /// @param liquidity The current in-range pool liquidity + /// @param cardinality The number of populated elements in the oracle array + /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo` + /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo` + function observe( + Observation[65535] storage self, + uint32 time, + uint32[] memory secondsAgos, + int24 tick, + uint16 index, + uint128 liquidity, + uint16 cardinality + ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) { + require(cardinality > 0, 'I'); + + tickCumulatives = new int56[](secondsAgos.length); + secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length); + for (uint256 i = 0; i < secondsAgos.length; i++) { + (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle( + self, + time, + secondsAgos[i], + tick, + index, + liquidity, + cardinality + ); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/Position.sol b/lib/uniswap/v3-core/contracts/libraries/Position.sol new file mode 100644 index 0000000..f75a40b --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/Position.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0 <0.8.0; + +import './FullMath.sol'; +import './FixedPoint128.sol'; +import './LiquidityMath.sol'; + +/// @title Position +/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary +/// @dev Positions store additional state for tracking fees owed to the position +library Position { + // info stored for each user's position + struct Info { + // the amount of liquidity owned by this position + uint128 liquidity; + // fee growth per unit of liquidity as of the last update to liquidity or fees owed + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + // the fees owed to the position owner in token0/token1 + uint128 tokensOwed0; + uint128 tokensOwed1; + } + + /// @notice Returns the Info struct of a position, given an owner and position boundaries + /// @param self The mapping containing all user positions + /// @param owner The address of the position owner + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @return position The position info struct of the given owners' position + function get( + mapping(bytes32 => Info) storage self, + address owner, + int24 tickLower, + int24 tickUpper + ) internal view returns (Position.Info storage position) { + position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))]; + } + + /// @notice Credits accumulated fees to a user's position + /// @param self The individual position to update + /// @param liquidityDelta The change in pool liquidity as a result of the position update + /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function update( + Info storage self, + int128 liquidityDelta, + uint256 feeGrowthInside0X128, + uint256 feeGrowthInside1X128 + ) internal { + Info memory _self = self; + + uint128 liquidityNext; + if (liquidityDelta == 0) { + require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions + liquidityNext = _self.liquidity; + } else { + liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta); + } + + // calculate accumulated fees + uint128 tokensOwed0 = uint128( + FullMath.mulDiv(feeGrowthInside0X128 - _self.feeGrowthInside0LastX128, _self.liquidity, FixedPoint128.Q128) + ); + uint128 tokensOwed1 = uint128( + FullMath.mulDiv(feeGrowthInside1X128 - _self.feeGrowthInside1LastX128, _self.liquidity, FixedPoint128.Q128) + ); + + // update the position + if (liquidityDelta != 0) self.liquidity = liquidityNext; + self.feeGrowthInside0LastX128 = feeGrowthInside0X128; + self.feeGrowthInside1LastX128 = feeGrowthInside1X128; + if (tokensOwed0 > 0 || tokensOwed1 > 0) { + // overflow is acceptable, have to withdraw before you hit type(uint128).max fees + self.tokensOwed0 += tokensOwed0; + self.tokensOwed1 += tokensOwed1; + } + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/SafeCast.sol b/lib/uniswap/v3-core/contracts/libraries/SafeCast.sol new file mode 100644 index 0000000..a8ea229 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/SafeCast.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Safe casting methods +/// @notice Contains methods for safely casting between types +library SafeCast { + /// @notice Cast a uint256 to a uint160, revert on overflow + /// @param y The uint256 to be downcasted + /// @return z The downcasted integer, now type uint160 + function toUint160(uint256 y) internal pure returns (uint160 z) { + require((z = uint160(y)) == y); + } + + /// @notice Cast a int256 to a int128, revert on overflow or underflow + /// @param y The int256 to be downcasted + /// @return z The downcasted integer, now type int128 + function toInt128(int256 y) internal pure returns (int128 z) { + require((z = int128(y)) == y); + } + + /// @notice Cast a uint256 to a int256, revert on overflow + /// @param y The uint256 to be casted + /// @return z The casted integer, now type int256 + function toInt256(uint256 y) internal pure returns (int256 z) { + require(y < 2**255); + z = int256(y); + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/SqrtPriceMath.sol b/lib/uniswap/v3-core/contracts/libraries/SqrtPriceMath.sol new file mode 100644 index 0000000..169ac9d --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/SqrtPriceMath.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; + +import './FullMath.sol'; +import './UnsafeMath.sol'; +import './FixedPoint96.sol'; + +/// @title Functions based on Q64.96 sqrt price and liquidity +/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas +library SqrtPriceMath { + using LowGasSafeMath for uint256; + using SafeCast for uint256; + + /// @notice Gets the next sqrt price given a delta of token0 + /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the + /// price less in order to not send too much output. + /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96), + /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount). + /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token0 to add or remove from virtual reserves + /// @param add Whether to add or remove the amount of token0 + /// @return The price after adding or removing amount, depending on add + function getNextSqrtPriceFromAmount0RoundingUp( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price + if (amount == 0) return sqrtPX96; + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + + if (add) { + uint256 product; + if ((product = amount * sqrtPX96) / amount == sqrtPX96) { + uint256 denominator = numerator1 + product; + if (denominator >= numerator1) + // always fits in 160 bits + return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator)); + } + + return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount))); + } else { + uint256 product; + // if the product overflows, we know the denominator underflows + // in addition, we must check that the denominator does not underflow + require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); + uint256 denominator = numerator1 - product; + return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); + } + } + + /// @notice Gets the next sqrt price given a delta of token1 + /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least + /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the + /// price less in order to not send too much output. + /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity + /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta + /// @param liquidity The amount of usable liquidity + /// @param amount How much of token1 to add, or remove, from virtual reserves + /// @param add Whether to add, or remove, the amount of token1 + /// @return The price after adding or removing `amount` + function getNextSqrtPriceFromAmount1RoundingDown( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) internal pure returns (uint160) { + // if we're adding (subtracting), rounding down requires rounding the quotient down (up) + // in both cases, avoid a mulDiv for most inputs + if (add) { + uint256 quotient = ( + amount <= type(uint160).max + ? (amount << FixedPoint96.RESOLUTION) / liquidity + : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity) + ); + + return uint256(sqrtPX96).add(quotient).toUint160(); + } else { + uint256 quotient = ( + amount <= type(uint160).max + ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity) + : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) + ); + + require(sqrtPX96 > quotient); + // always fits 160 bits + return uint160(sqrtPX96 - quotient); + } + } + + /// @notice Gets the next sqrt price given an input amount of token0 or token1 + /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds + /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount + /// @param liquidity The amount of usable liquidity + /// @param amountIn How much of token0, or token1, is being swapped in + /// @param zeroForOne Whether the amount in is token0 or token1 + /// @return sqrtQX96 The price after adding the input amount to token0 or token1 + function getNextSqrtPriceFromInput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we don't pass the target price + return + zeroForOne + ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) + : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true); + } + + /// @notice Gets the next sqrt price given an output amount of token0 or token1 + /// @dev Throws if price or liquidity are 0 or the next price is out of bounds + /// @param sqrtPX96 The starting price before accounting for the output amount + /// @param liquidity The amount of usable liquidity + /// @param amountOut How much of token0, or token1, is being swapped out + /// @param zeroForOne Whether the amount out is token0 or token1 + /// @return sqrtQX96 The price after removing the output amount of token0 or token1 + function getNextSqrtPriceFromOutput( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) internal pure returns (uint160 sqrtQX96) { + require(sqrtPX96 > 0); + require(liquidity > 0); + + // round to make sure that we pass the target price + return + zeroForOne + ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) + : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false); + } + + /// @notice Gets the amount0 delta between two prices + /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), + /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up or down + /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount0) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; + + require(sqrtRatioAX96 > 0); + + return + roundUp + ? UnsafeMath.divRoundingUp( + FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), + sqrtRatioAX96 + ) + : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; + } + + /// @notice Gets the amount1 delta between two prices + /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up, or down + /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + return + roundUp + ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) + : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); + } + + /// @notice Helper that gets signed token0 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount0 delta + /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + int128 liquidity + ) internal pure returns (int256 amount0) { + return + liquidity < 0 + ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } + + /// @notice Helper that gets signed token1 delta + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The change in liquidity for which to compute the amount1 delta + /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + int128 liquidity + ) internal pure returns (int256 amount1) { + return + liquidity < 0 + ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256() + : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256(); + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/SwapMath.sol b/lib/uniswap/v3-core/contracts/libraries/SwapMath.sol new file mode 100644 index 0000000..ee176fb --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/SwapMath.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './FullMath.sol'; +import './SqrtPriceMath.sol'; + +/// @title Computes the result of a swap within ticks +/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick. +library SwapMath { + /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap + /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive + /// @param sqrtRatioCurrentX96 The current sqrt price of the pool + /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred + /// @param liquidity The usable liquidity + /// @param amountRemaining How much input or output amount is remaining to be swapped in/out + /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip + /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target + /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap + /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap + /// @return feeAmount The amount of input that will be taken as a fee + function computeSwapStep( + uint160 sqrtRatioCurrentX96, + uint160 sqrtRatioTargetX96, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) + internal + pure + returns ( + uint160 sqrtRatioNextX96, + uint256 amountIn, + uint256 amountOut, + uint256 feeAmount + ) + { + bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96; + bool exactIn = amountRemaining >= 0; + + if (exactIn) { + uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6); + amountIn = zeroForOne + ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true) + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true); + if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96; + else + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput( + sqrtRatioCurrentX96, + liquidity, + amountRemainingLessFee, + zeroForOne + ); + } else { + amountOut = zeroForOne + ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false) + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false); + if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96; + else + sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput( + sqrtRatioCurrentX96, + liquidity, + uint256(-amountRemaining), + zeroForOne + ); + } + + bool max = sqrtRatioTargetX96 == sqrtRatioNextX96; + + // get the input/output amounts + if (zeroForOne) { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false); + } else { + amountIn = max && exactIn + ? amountIn + : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true); + amountOut = max && !exactIn + ? amountOut + : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false); + } + + // cap the output amount to not exceed the remaining output amount + if (!exactIn && amountOut > uint256(-amountRemaining)) { + amountOut = uint256(-amountRemaining); + } + + if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) { + // we didn't reach the target, so take the remainder of the maximum input as fee + feeAmount = uint256(amountRemaining) - amountIn; + } else { + feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/Tick.sol b/lib/uniswap/v3-core/contracts/libraries/Tick.sol new file mode 100644 index 0000000..7ceed03 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/Tick.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0 <0.8.0; + +import './LowGasSafeMath.sol'; +import './SafeCast.sol'; + +import './TickMath.sol'; +import './LiquidityMath.sol'; + +/// @title Tick +/// @notice Contains functions for managing tick processes and relevant calculations +library Tick { + using LowGasSafeMath for int256; + using SafeCast for int256; + + // info stored for each initialized individual tick + struct Info { + // the total position liquidity that references this tick + uint128 liquidityGross; + // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left), + int128 liquidityNet; + // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint256 feeGrowthOutside0X128; + uint256 feeGrowthOutside1X128; + // the cumulative tick value on the other side of the tick + int56 tickCumulativeOutside; + // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint160 secondsPerLiquidityOutsideX128; + // the seconds spent on the other side of the tick (relative to the current tick) + // only has relative meaning, not absolute — the value depends on when the tick is initialized + uint32 secondsOutside; + // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0 + // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks + bool initialized; + } + + /// @notice Derives max liquidity per tick from given tick spacing + /// @dev Executed within the pool constructor + /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing` + /// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ... + /// @return The max liquidity per tick + function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) { + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1; + return type(uint128).max / numTicks; + } + + /// @notice Retrieves fee growth data + /// @param self The mapping containing all tick information for initialized ticks + /// @param tickLower The lower tick boundary of the position + /// @param tickUpper The upper tick boundary of the position + /// @param tickCurrent The current tick + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries + /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries + function getFeeGrowthInside( + mapping(int24 => Tick.Info) storage self, + int24 tickLower, + int24 tickUpper, + int24 tickCurrent, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128 + ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + Info storage lower = self[tickLower]; + Info storage upper = self[tickUpper]; + + // calculate fee growth below + uint256 feeGrowthBelow0X128; + uint256 feeGrowthBelow1X128; + if (tickCurrent >= tickLower) { + feeGrowthBelow0X128 = lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = lower.feeGrowthOutside1X128; + } else { + feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128; + feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128; + } + + // calculate fee growth above + uint256 feeGrowthAbove0X128; + uint256 feeGrowthAbove1X128; + if (tickCurrent < tickUpper) { + feeGrowthAbove0X128 = upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = upper.feeGrowthOutside1X128; + } else { + feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128; + feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128; + } + + feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128; + feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128; + } + + /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The tick that will be updated + /// @param tickCurrent The current tick + /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left) + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool + /// @param tickCumulative The tick * time elapsed since the pool was first initialized + /// @param time The current block timestamp cast to a uint32 + /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick + /// @param maxLiquidity The maximum liquidity allocation for a single tick + /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa + function update( + mapping(int24 => Tick.Info) storage self, + int24 tick, + int24 tickCurrent, + int128 liquidityDelta, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time, + bool upper, + uint128 maxLiquidity + ) internal returns (bool flipped) { + Tick.Info storage info = self[tick]; + + uint128 liquidityGrossBefore = info.liquidityGross; + uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta); + + require(liquidityGrossAfter <= maxLiquidity, 'LO'); + + flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0); + + if (liquidityGrossBefore == 0) { + // by convention, we assume that all growth before a tick was initialized happened _below_ the tick + if (tick <= tickCurrent) { + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128; + info.tickCumulativeOutside = tickCumulative; + info.secondsOutside = time; + } + info.initialized = true; + } + + info.liquidityGross = liquidityGrossAfter; + + // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed) + info.liquidityNet = upper + ? int256(info.liquidityNet).sub(liquidityDelta).toInt128() + : int256(info.liquidityNet).add(liquidityDelta).toInt128(); + } + + /// @notice Clears tick data + /// @param self The mapping containing all initialized tick information for initialized ticks + /// @param tick The tick that will be cleared + function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal { + delete self[tick]; + } + + /// @notice Transitions to next tick as needed by price movement + /// @param self The mapping containing all tick information for initialized ticks + /// @param tick The destination tick of the transition + /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0 + /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1 + /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity + /// @param tickCumulative The tick * time elapsed since the pool was first initialized + /// @param time The current block.timestamp + /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left) + function cross( + mapping(int24 => Tick.Info) storage self, + int24 tick, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time + ) internal returns (int128 liquidityNet) { + Tick.Info storage info = self[tick]; + info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128; + info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128; + info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128; + info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside; + info.secondsOutside = time - info.secondsOutside; + liquidityNet = info.liquidityNet; + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/TickBitmap.sol b/lib/uniswap/v3-core/contracts/libraries/TickBitmap.sol new file mode 100644 index 0000000..3c43585 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/TickBitmap.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import './BitMath.sol'; + +/// @title Packed tick initialized state library +/// @notice Stores a packed mapping of tick index to its initialized state +/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word. +library TickBitmap { + /// @notice Computes the position in the mapping where the initialized bit for a tick lives + /// @param tick The tick for which to compute the position + /// @return wordPos The key in the mapping containing the word in which the bit is stored + /// @return bitPos The bit position in the word where the flag is stored + function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) { + wordPos = int16(tick >> 8); + bitPos = uint8(tick % 256); + } + + /// @notice Flips the initialized state for a given tick from false to true, or vice versa + /// @param self The mapping in which to flip the tick + /// @param tick The tick to flip + /// @param tickSpacing The spacing between usable ticks + function flipTick( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing + ) internal { + require(tick % tickSpacing == 0); // ensure that the tick is spaced + (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing); + uint256 mask = 1 << bitPos; + self[wordPos] ^= mask; + } + + /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either + /// to the left (less than or equal to) or right (greater than) of the given tick + /// @param self The mapping in which to compute the next initialized tick + /// @param tick The starting tick + /// @param tickSpacing The spacing between usable ticks + /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick) + /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick + /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks + function nextInitializedTickWithinOneWord( + mapping(int16 => uint256) storage self, + int24 tick, + int24 tickSpacing, + bool lte + ) internal view returns (int24 next, bool initialized) { + int24 compressed = tick / tickSpacing; + if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity + + if (lte) { + (int16 wordPos, uint8 bitPos) = position(compressed); + // all the 1s at or to the right of the current bitPos + uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing + : (compressed - int24(bitPos)) * tickSpacing; + } else { + // start from the word of the next tick, since the current tick state doesn't matter + (int16 wordPos, uint8 bitPos) = position(compressed + 1); + // all the 1s at or to the left of the bitPos + uint256 mask = ~((1 << bitPos) - 1); + uint256 masked = self[wordPos] & mask; + + // if there are no initialized ticks to the left of the current tick, return leftmost in the word + initialized = masked != 0; + // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick + next = initialized + ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing + : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing; + } + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/TickMath.sol b/lib/uniswap/v3-core/contracts/libraries/TickMath.sol new file mode 100644 index 0000000..ee48fee --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/TickMath.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0 <0.8.0; + +/// @title Math library for computing sqrt prices from ticks and vice versa +/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports +/// prices between 2**-128 and 2**128 +library TickMath { + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + + /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) + uint160 internal constant MIN_SQRT_RATIO = 4295128739; + /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) + uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + + /// @notice Calculates sqrt(1.0001^tick) * 2^96 + /// @dev Throws if |tick| > max tick + /// @param tick The input tick for the above formula + /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) + /// at the given tick + function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { + uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); + require(absTick <= uint256(MAX_TICK), 'T'); + + uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; + if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; + if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; + if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; + if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; + if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; + if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; + if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; + if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; + if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; + if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; + if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; + if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; + if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; + if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; + if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; + if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; + if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; + if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; + if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; + + if (tick > 0) ratio = type(uint256).max / ratio; + + // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. + // we then downcast because we know the result always fits within 160 bits due to our tick input constraint + // we round up in the division so getTickAtSqrtRatio of the output price is always consistent + sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); + } + + /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio + /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may + /// ever return. + /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 + /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio + function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { + // second inequality must be < because the price can never reach the price at the max tick + require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); + uint256 ratio = uint256(sqrtPriceX96) << 32; + + uint256 r = ratio; + uint256 msb = 0; + + assembly { + let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(5, gt(r, 0xFFFFFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(4, gt(r, 0xFFFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(3, gt(r, 0xFF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(2, gt(r, 0xF)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := shl(1, gt(r, 0x3)) + msb := or(msb, f) + r := shr(f, r) + } + assembly { + let f := gt(r, 0x1) + msb := or(msb, f) + } + + if (msb >= 128) r = ratio >> (msb - 127); + else r = ratio << (127 - msb); + + int256 log_2 = (int256(msb) - 128) << 64; + + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(63, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(62, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(61, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(60, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(59, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(58, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(57, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(56, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(55, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(54, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(53, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(52, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(51, f)) + r := shr(f, r) + } + assembly { + r := shr(127, mul(r, r)) + let f := shr(128, r) + log_2 := or(log_2, shl(50, f)) + } + + int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number + + int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); + int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); + + tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/TransferHelper.sol b/lib/uniswap/v3-core/contracts/libraries/TransferHelper.sol new file mode 100644 index 0000000..4e21732 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/TransferHelper.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import '../interfaces/IERC20Minimal.sol'; + +/// @title TransferHelper +/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false +library TransferHelper { + /// @notice Transfers tokens from msg.sender to a recipient + /// @dev Calls transfer on token contract, errors with TF if transfer fails + /// @param token The contract address of the token which will be transferred + /// @param to The recipient of the transfer + /// @param value The value of the transfer + function safeTransfer( + address token, + address to, + uint256 value + ) internal { + (bool success, bytes memory data) = token.call( + abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value) + ); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF'); + } +} diff --git a/lib/uniswap/v3-core/contracts/libraries/UnsafeMath.sol b/lib/uniswap/v3-core/contracts/libraries/UnsafeMath.sol new file mode 100644 index 0000000..f62f846 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/libraries/UnsafeMath.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Math functions that do not check inputs or outputs +/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks +library UnsafeMath { + /// @notice Returns ceil(x / y) + /// @dev division by 0 has unspecified behavior, and must be checked externally + /// @param x The dividend + /// @param y The divisor + /// @return z The quotient, ceil(x / y) + function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) { + assembly { + z := add(div(x, y), gt(mod(x, y), 0)) + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/BitMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/BitMathEchidnaTest.sol new file mode 100644 index 0000000..ca7d34f --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/BitMathEchidnaTest.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/BitMath.sol'; + +contract BitMathEchidnaTest { + function mostSignificantBitInvariant(uint256 input) external pure { + uint8 msb = BitMath.mostSignificantBit(input); + assert(input >= (uint256(2)**msb)); + assert(msb == 255 || input < uint256(2)**(msb + 1)); + } + + function leastSignificantBitInvariant(uint256 input) external pure { + uint8 lsb = BitMath.leastSignificantBit(input); + assert(input & (uint256(2)**lsb) != 0); + assert(input & (uint256(2)**lsb - 1) == 0); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/BitMathTest.sol b/lib/uniswap/v3-core/contracts/test/BitMathTest.sol new file mode 100644 index 0000000..2b387be --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/BitMathTest.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/BitMath.sol'; + +contract BitMathTest { + function mostSignificantBit(uint256 x) external pure returns (uint8 r) { + return BitMath.mostSignificantBit(x); + } + + function getGasCostOfMostSignificantBit(uint256 x) external view returns (uint256) { + uint256 gasBefore = gasleft(); + BitMath.mostSignificantBit(x); + return gasBefore - gasleft(); + } + + function leastSignificantBit(uint256 x) external pure returns (uint8 r) { + return BitMath.leastSignificantBit(x); + } + + function getGasCostOfLeastSignificantBit(uint256 x) external view returns (uint256) { + uint256 gasBefore = gasleft(); + BitMath.leastSignificantBit(x); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/FullMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/FullMathEchidnaTest.sol new file mode 100644 index 0000000..9e1063d --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/FullMathEchidnaTest.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/FullMath.sol'; + +contract FullMathEchidnaTest { + function checkMulDivRounding( + uint256 x, + uint256 y, + uint256 d + ) external pure { + require(d > 0); + + uint256 ceiled = FullMath.mulDivRoundingUp(x, y, d); + uint256 floored = FullMath.mulDiv(x, y, d); + + if (mulmod(x, y, d) > 0) { + assert(ceiled - floored == 1); + } else { + assert(ceiled == floored); + } + } + + function checkMulDiv( + uint256 x, + uint256 y, + uint256 d + ) external pure { + require(d > 0); + uint256 z = FullMath.mulDiv(x, y, d); + if (x == 0 || y == 0) { + assert(z == 0); + return; + } + + // recompute x and y via mulDiv of the result of floor(x*y/d), should always be less than original inputs by < d + uint256 x2 = FullMath.mulDiv(z, d, y); + uint256 y2 = FullMath.mulDiv(z, d, x); + assert(x2 <= x); + assert(y2 <= y); + + assert(x - x2 < d); + assert(y - y2 < d); + } + + function checkMulDivRoundingUp( + uint256 x, + uint256 y, + uint256 d + ) external pure { + require(d > 0); + uint256 z = FullMath.mulDivRoundingUp(x, y, d); + if (x == 0 || y == 0) { + assert(z == 0); + return; + } + + // recompute x and y via mulDiv of the result of floor(x*y/d), should always be less than original inputs by < d + uint256 x2 = FullMath.mulDiv(z, d, y); + uint256 y2 = FullMath.mulDiv(z, d, x); + assert(x2 >= x); + assert(y2 >= y); + + assert(x2 - x < d); + assert(y2 - y < d); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/FullMathTest.sol b/lib/uniswap/v3-core/contracts/test/FullMathTest.sol new file mode 100644 index 0000000..ec59bc9 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/FullMathTest.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/FullMath.sol'; + +contract FullMathTest { + function mulDiv( + uint256 x, + uint256 y, + uint256 z + ) external pure returns (uint256) { + return FullMath.mulDiv(x, y, z); + } + + function mulDivRoundingUp( + uint256 x, + uint256 y, + uint256 z + ) external pure returns (uint256) { + return FullMath.mulDivRoundingUp(x, y, z); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/LiquidityMathTest.sol b/lib/uniswap/v3-core/contracts/test/LiquidityMathTest.sol new file mode 100644 index 0000000..d85e0f1 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/LiquidityMathTest.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/LiquidityMath.sol'; + +contract LiquidityMathTest { + function addDelta(uint128 x, int128 y) external pure returns (uint128 z) { + return LiquidityMath.addDelta(x, y); + } + + function getGasCostOfAddDelta(uint128 x, int128 y) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityMath.addDelta(x, y); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/LowGasSafeMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/LowGasSafeMathEchidnaTest.sol new file mode 100644 index 0000000..315531d --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/LowGasSafeMathEchidnaTest.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/LowGasSafeMath.sol'; + +contract LowGasSafeMathEchidnaTest { + function checkAdd(uint256 x, uint256 y) external pure { + uint256 z = LowGasSafeMath.add(x, y); + assert(z == x + y); + assert(z >= x && z >= y); + } + + function checkSub(uint256 x, uint256 y) external pure { + uint256 z = LowGasSafeMath.sub(x, y); + assert(z == x - y); + assert(z <= x); + } + + function checkMul(uint256 x, uint256 y) external pure { + uint256 z = LowGasSafeMath.mul(x, y); + assert(z == x * y); + assert(x == 0 || y == 0 || (z >= x && z >= y)); + } + + function checkAddi(int256 x, int256 y) external pure { + int256 z = LowGasSafeMath.add(x, y); + assert(z == x + y); + assert(y < 0 ? z < x : z >= x); + } + + function checkSubi(int256 x, int256 y) external pure { + int256 z = LowGasSafeMath.sub(x, y); + assert(z == x - y); + assert(y < 0 ? z > x : z <= x); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/MockTimeUniswapV3Pool.sol b/lib/uniswap/v3-core/contracts/test/MockTimeUniswapV3Pool.sol new file mode 100644 index 0000000..66d2566 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/MockTimeUniswapV3Pool.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../UniswapV3Pool.sol'; + +// used for testing time dependent behavior +contract MockTimeUniswapV3Pool is UniswapV3Pool { + // Monday, October 5, 2020 9:00:00 AM GMT-05:00 + uint256 public time = 1601906400; + + function setFeeGrowthGlobal0X128(uint256 _feeGrowthGlobal0X128) external { + feeGrowthGlobal0X128 = _feeGrowthGlobal0X128; + } + + function setFeeGrowthGlobal1X128(uint256 _feeGrowthGlobal1X128) external { + feeGrowthGlobal1X128 = _feeGrowthGlobal1X128; + } + + function advanceTime(uint256 by) external { + time += by; + } + + function _blockTimestamp() internal view override returns (uint32) { + return uint32(time); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/MockTimeUniswapV3PoolDeployer.sol b/lib/uniswap/v3-core/contracts/test/MockTimeUniswapV3PoolDeployer.sol new file mode 100644 index 0000000..a274fb7 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/MockTimeUniswapV3PoolDeployer.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/IUniswapV3PoolDeployer.sol'; + +import './MockTimeUniswapV3Pool.sol'; + +contract MockTimeUniswapV3PoolDeployer is IUniswapV3PoolDeployer { + struct Parameters { + address factory; + address token0; + address token1; + uint24 fee; + int24 tickSpacing; + } + + Parameters public override parameters; + + event PoolDeployed(address pool); + + function deploy( + address factory, + address token0, + address token1, + uint24 fee, + int24 tickSpacing + ) external returns (address pool) { + parameters = Parameters({factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing}); + pool = address( + new MockTimeUniswapV3Pool{salt: keccak256(abi.encodePacked(token0, token1, fee, tickSpacing))}() + ); + emit PoolDeployed(pool); + delete parameters; + } +} diff --git a/lib/uniswap/v3-core/contracts/test/NoDelegateCallTest.sol b/lib/uniswap/v3-core/contracts/test/NoDelegateCallTest.sol new file mode 100644 index 0000000..8830d5c --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/NoDelegateCallTest.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../NoDelegateCall.sol'; + +contract NoDelegateCallTest is NoDelegateCall { + function canBeDelegateCalled() public view returns (uint256) { + return block.timestamp / 5; + } + + function cannotBeDelegateCalled() public view noDelegateCall returns (uint256) { + return block.timestamp / 5; + } + + function getGasCostOfCanBeDelegateCalled() external view returns (uint256) { + uint256 gasBefore = gasleft(); + canBeDelegateCalled(); + return gasBefore - gasleft(); + } + + function getGasCostOfCannotBeDelegateCalled() external view returns (uint256) { + uint256 gasBefore = gasleft(); + cannotBeDelegateCalled(); + return gasBefore - gasleft(); + } + + function callsIntoNoDelegateCallFunction() external view { + noDelegateCallPrivate(); + } + + function noDelegateCallPrivate() private view noDelegateCall {} +} diff --git a/lib/uniswap/v3-core/contracts/test/OracleEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/OracleEchidnaTest.sol new file mode 100644 index 0000000..4e0a7df --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/OracleEchidnaTest.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import './OracleTest.sol'; + +contract OracleEchidnaTest { + OracleTest private oracle; + + bool private initialized; + uint32 private timePassed; + + constructor() { + oracle = new OracleTest(); + } + + function initialize( + uint32 time, + int24 tick, + uint128 liquidity + ) external { + oracle.initialize(OracleTest.InitializeParams({time: time, tick: tick, liquidity: liquidity})); + initialized = true; + } + + function limitTimePassed(uint32 by) private { + require(timePassed + by >= timePassed); + timePassed += by; + } + + function advanceTime(uint32 by) public { + limitTimePassed(by); + oracle.advanceTime(by); + } + + // write an observation, then change tick and liquidity + function update( + uint32 advanceTimeBy, + int24 tick, + uint128 liquidity + ) external { + limitTimePassed(advanceTimeBy); + oracle.update(OracleTest.UpdateParams({advanceTimeBy: advanceTimeBy, tick: tick, liquidity: liquidity})); + } + + function grow(uint16 cardinality) external { + oracle.grow(cardinality); + } + + function checkTimeWeightedResultAssertions(uint32 secondsAgo0, uint32 secondsAgo1) private view { + require(secondsAgo0 != secondsAgo1); + require(initialized); + // secondsAgo0 should be the larger one + if (secondsAgo0 < secondsAgo1) (secondsAgo0, secondsAgo1) = (secondsAgo1, secondsAgo0); + + uint32 timeElapsed = secondsAgo0 - secondsAgo1; + + uint32[] memory secondsAgos = new uint32[](2); + secondsAgos[0] = secondsAgo0; + secondsAgos[1] = secondsAgo1; + + (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = oracle.observe( + secondsAgos + ); + int56 timeWeightedTick = (tickCumulatives[1] - tickCumulatives[0]) / timeElapsed; + uint256 timeWeightedHarmonicMeanLiquidity = (uint256(timeElapsed) * type(uint160).max) / + (uint256(secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0]) << 32); + assert(timeWeightedHarmonicMeanLiquidity <= type(uint128).max); + assert(timeWeightedTick <= type(int24).max); + assert(timeWeightedTick >= type(int24).min); + } + + function echidna_indexAlwaysLtCardinality() external view returns (bool) { + return oracle.index() < oracle.cardinality() || !initialized; + } + + function echidna_AlwaysInitialized() external view returns (bool) { + (, , , bool isInitialized) = oracle.observations(0); + return oracle.cardinality() == 0 || isInitialized; + } + + function echidna_cardinalityAlwaysLteNext() external view returns (bool) { + return oracle.cardinality() <= oracle.cardinalityNext(); + } + + function echidna_canAlwaysObserve0IfInitialized() external view returns (bool) { + if (!initialized) { + return true; + } + uint32[] memory arr = new uint32[](1); + arr[0] = 0; + (bool success, ) = address(oracle).staticcall(abi.encodeWithSelector(OracleTest.observe.selector, arr)); + return success; + } + + function checkTwoAdjacentObservationsTickCumulativeModTimeElapsedAlways0(uint16 index) external view { + uint16 cardinality = oracle.cardinality(); + // check that the observations are initialized, and that the index is not the oldest observation + require(index < cardinality && index != (oracle.index() + 1) % cardinality); + + (uint32 blockTimestamp0, int56 tickCumulative0, , bool initialized0) = oracle.observations( + index == 0 ? cardinality - 1 : index - 1 + ); + (uint32 blockTimestamp1, int56 tickCumulative1, , bool initialized1) = oracle.observations(index); + + require(initialized0); + require(initialized1); + + uint32 timeElapsed = blockTimestamp1 - blockTimestamp0; + assert(timeElapsed > 0); + assert((tickCumulative1 - tickCumulative0) % timeElapsed == 0); + } + + function checkTimeWeightedAveragesAlwaysFitsType(uint32 secondsAgo) external view { + require(initialized); + require(secondsAgo > 0); + uint32[] memory secondsAgos = new uint32[](2); + secondsAgos[0] = secondsAgo; + secondsAgos[1] = 0; + (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = oracle.observe( + secondsAgos + ); + + // compute the time weighted tick, rounded towards negative infinity + int56 numerator = tickCumulatives[1] - tickCumulatives[0]; + int56 timeWeightedTick = numerator / int56(secondsAgo); + if (numerator < 0 && numerator % int56(secondsAgo) != 0) { + timeWeightedTick--; + } + + // the time weighted averages fit in their respective accumulated types + assert(timeWeightedTick <= type(int24).max && timeWeightedTick >= type(int24).min); + + uint256 timeWeightedHarmonicMeanLiquidity = (uint256(secondsAgo) * type(uint160).max) / + (uint256(secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0]) << 32); + assert(timeWeightedHarmonicMeanLiquidity <= type(uint128).max); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/OracleTest.sol b/lib/uniswap/v3-core/contracts/test/OracleTest.sol new file mode 100644 index 0000000..c0c83b1 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/OracleTest.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../libraries/Oracle.sol'; + +contract OracleTest { + using Oracle for Oracle.Observation[65535]; + + Oracle.Observation[65535] public observations; + + uint32 public time; + int24 public tick; + uint128 public liquidity; + uint16 public index; + uint16 public cardinality; + uint16 public cardinalityNext; + + struct InitializeParams { + uint32 time; + int24 tick; + uint128 liquidity; + } + + function initialize(InitializeParams calldata params) external { + require(cardinality == 0, 'already initialized'); + time = params.time; + tick = params.tick; + liquidity = params.liquidity; + (cardinality, cardinalityNext) = observations.initialize(params.time); + } + + function advanceTime(uint32 by) public { + time += by; + } + + struct UpdateParams { + uint32 advanceTimeBy; + int24 tick; + uint128 liquidity; + } + + // write an observation, then change tick and liquidity + function update(UpdateParams calldata params) external { + advanceTime(params.advanceTimeBy); + (index, cardinality) = observations.write(index, time, tick, liquidity, cardinality, cardinalityNext); + tick = params.tick; + liquidity = params.liquidity; + } + + function batchUpdate(UpdateParams[] calldata params) external { + // sload everything + int24 _tick = tick; + uint128 _liquidity = liquidity; + uint16 _index = index; + uint16 _cardinality = cardinality; + uint16 _cardinalityNext = cardinalityNext; + uint32 _time = time; + + for (uint256 i = 0; i < params.length; i++) { + _time += params[i].advanceTimeBy; + (_index, _cardinality) = observations.write( + _index, + _time, + _tick, + _liquidity, + _cardinality, + _cardinalityNext + ); + _tick = params[i].tick; + _liquidity = params[i].liquidity; + } + + // sstore everything + tick = _tick; + liquidity = _liquidity; + index = _index; + cardinality = _cardinality; + time = _time; + } + + function grow(uint16 _cardinalityNext) external { + cardinalityNext = observations.grow(cardinalityNext, _cardinalityNext); + } + + function observe(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) + { + return observations.observe(time, secondsAgos, tick, index, liquidity, cardinality); + } + + function getGasCostOfObserve(uint32[] calldata secondsAgos) external view returns (uint256) { + (uint32 _time, int24 _tick, uint128 _liquidity, uint16 _index) = (time, tick, liquidity, index); + uint256 gasBefore = gasleft(); + observations.observe(_time, secondsAgos, _tick, _index, _liquidity, cardinality); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/SqrtPriceMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/SqrtPriceMathEchidnaTest.sol new file mode 100644 index 0000000..1e569ac --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/SqrtPriceMathEchidnaTest.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/FullMath.sol'; +import '../libraries/SqrtPriceMath.sol'; +import '../libraries/FixedPoint96.sol'; + +contract SqrtPriceMathEchidnaTest { + function mulDivRoundingUpInvariants( + uint256 x, + uint256 y, + uint256 z + ) external pure { + require(z > 0); + uint256 notRoundedUp = FullMath.mulDiv(x, y, z); + uint256 roundedUp = FullMath.mulDivRoundingUp(x, y, z); + assert(roundedUp >= notRoundedUp); + assert(roundedUp - notRoundedUp < 2); + if (roundedUp - notRoundedUp == 1) { + assert(mulmod(x, y, z) > 0); + } else { + assert(mulmod(x, y, z) == 0); + } + } + + function getNextSqrtPriceFromInputInvariants( + uint160 sqrtP, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) external pure { + uint160 sqrtQ = SqrtPriceMath.getNextSqrtPriceFromInput(sqrtP, liquidity, amountIn, zeroForOne); + + if (zeroForOne) { + assert(sqrtQ <= sqrtP); + assert(amountIn >= SqrtPriceMath.getAmount0Delta(sqrtQ, sqrtP, liquidity, true)); + } else { + assert(sqrtQ >= sqrtP); + assert(amountIn >= SqrtPriceMath.getAmount1Delta(sqrtP, sqrtQ, liquidity, true)); + } + } + + function getNextSqrtPriceFromOutputInvariants( + uint160 sqrtP, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) external pure { + uint160 sqrtQ = SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtP, liquidity, amountOut, zeroForOne); + + if (zeroForOne) { + assert(sqrtQ <= sqrtP); + assert(amountOut <= SqrtPriceMath.getAmount1Delta(sqrtQ, sqrtP, liquidity, false)); + } else { + assert(sqrtQ > 0); // this has to be true, otherwise we need another require + assert(sqrtQ >= sqrtP); + assert(amountOut <= SqrtPriceMath.getAmount0Delta(sqrtP, sqrtQ, liquidity, false)); + } + } + + function getNextSqrtPriceFromAmount0RoundingUpInvariants( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) external pure { + require(sqrtPX96 > 0); + require(liquidity > 0); + uint160 sqrtQX96 = SqrtPriceMath.getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amount, add); + + if (add) { + assert(sqrtQX96 <= sqrtPX96); + } else { + assert(sqrtQX96 >= sqrtPX96); + } + + if (amount == 0) { + assert(sqrtPX96 == sqrtQX96); + } + } + + function getNextSqrtPriceFromAmount1RoundingDownInvariants( + uint160 sqrtPX96, + uint128 liquidity, + uint256 amount, + bool add + ) external pure { + require(sqrtPX96 > 0); + require(liquidity > 0); + uint160 sqrtQX96 = SqrtPriceMath.getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amount, add); + + if (add) { + assert(sqrtQX96 >= sqrtPX96); + } else { + assert(sqrtQX96 <= sqrtPX96); + } + + if (amount == 0) { + assert(sqrtPX96 == sqrtQX96); + } + } + + function getAmount0DeltaInvariants( + uint160 sqrtP, + uint160 sqrtQ, + uint128 liquidity + ) external pure { + require(sqrtP > 0 && sqrtQ > 0); + + uint256 amount0Down = SqrtPriceMath.getAmount0Delta(sqrtQ, sqrtP, liquidity, false); + assert(amount0Down == SqrtPriceMath.getAmount0Delta(sqrtP, sqrtQ, liquidity, false)); + + uint256 amount0Up = SqrtPriceMath.getAmount0Delta(sqrtQ, sqrtP, liquidity, true); + assert(amount0Up == SqrtPriceMath.getAmount0Delta(sqrtP, sqrtQ, liquidity, true)); + + assert(amount0Down <= amount0Up); + // diff is 0 or 1 + assert(amount0Up - amount0Down < 2); + } + + // ensure that chained division is always equal to the full-precision case for + // liquidity * (sqrt(P) - sqrt(Q)) / (sqrt(P) * sqrt(Q)) + function getAmount0DeltaEquivalency( + uint160 sqrtP, + uint160 sqrtQ, + uint128 liquidity, + bool roundUp + ) external pure { + require(sqrtP >= sqrtQ); + require(sqrtP > 0 && sqrtQ > 0); + require((sqrtP * sqrtQ) / sqrtP == sqrtQ); + + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtP - sqrtQ; + uint256 denominator = uint256(sqrtP) * sqrtQ; + + uint256 safeResult = roundUp + ? FullMath.mulDivRoundingUp(numerator1, numerator2, denominator) + : FullMath.mulDiv(numerator1, numerator2, denominator); + uint256 fullResult = SqrtPriceMath.getAmount0Delta(sqrtQ, sqrtP, liquidity, roundUp); + + assert(safeResult == fullResult); + } + + function getAmount1DeltaInvariants( + uint160 sqrtP, + uint160 sqrtQ, + uint128 liquidity + ) external pure { + require(sqrtP > 0 && sqrtQ > 0); + + uint256 amount1Down = SqrtPriceMath.getAmount1Delta(sqrtP, sqrtQ, liquidity, false); + assert(amount1Down == SqrtPriceMath.getAmount1Delta(sqrtQ, sqrtP, liquidity, false)); + + uint256 amount1Up = SqrtPriceMath.getAmount1Delta(sqrtP, sqrtQ, liquidity, true); + assert(amount1Up == SqrtPriceMath.getAmount1Delta(sqrtQ, sqrtP, liquidity, true)); + + assert(amount1Down <= amount1Up); + // diff is 0 or 1 + assert(amount1Up - amount1Down < 2); + } + + function getAmount0DeltaSignedInvariants( + uint160 sqrtP, + uint160 sqrtQ, + int128 liquidity + ) external pure { + require(sqrtP > 0 && sqrtQ > 0); + + int256 amount0 = SqrtPriceMath.getAmount0Delta(sqrtQ, sqrtP, liquidity); + if (liquidity < 0) assert(amount0 <= 0); + if (liquidity > 0) { + if (sqrtP == sqrtQ) assert(amount0 == 0); + else assert(amount0 > 0); + } + if (liquidity == 0) assert(amount0 == 0); + } + + function getAmount1DeltaSignedInvariants( + uint160 sqrtP, + uint160 sqrtQ, + int128 liquidity + ) external pure { + require(sqrtP > 0 && sqrtQ > 0); + + int256 amount1 = SqrtPriceMath.getAmount1Delta(sqrtP, sqrtQ, liquidity); + if (liquidity < 0) assert(amount1 <= 0); + if (liquidity > 0) { + if (sqrtP == sqrtQ) assert(amount1 == 0); + else assert(amount1 > 0); + } + if (liquidity == 0) assert(amount1 == 0); + } + + function getOutOfRangeMintInvariants( + uint160 sqrtA, + uint160 sqrtB, + int128 liquidity + ) external pure { + require(sqrtA > 0 && sqrtB > 0); + require(liquidity > 0); + + int256 amount0 = SqrtPriceMath.getAmount0Delta(sqrtA, sqrtB, liquidity); + int256 amount1 = SqrtPriceMath.getAmount1Delta(sqrtA, sqrtB, liquidity); + + if (sqrtA == sqrtB) { + assert(amount0 == 0); + assert(amount1 == 0); + } else { + assert(amount0 > 0); + assert(amount1 > 0); + } + } + + function getInRangeMintInvariants( + uint160 sqrtLower, + uint160 sqrtCurrent, + uint160 sqrtUpper, + int128 liquidity + ) external pure { + require(sqrtLower > 0); + require(sqrtLower < sqrtUpper); + require(sqrtLower <= sqrtCurrent && sqrtCurrent <= sqrtUpper); + require(liquidity > 0); + + int256 amount0 = SqrtPriceMath.getAmount0Delta(sqrtCurrent, sqrtUpper, liquidity); + int256 amount1 = SqrtPriceMath.getAmount1Delta(sqrtLower, sqrtCurrent, liquidity); + + assert(amount0 > 0 || amount1 > 0); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/SqrtPriceMathTest.sol b/lib/uniswap/v3-core/contracts/test/SqrtPriceMathTest.sol new file mode 100644 index 0000000..41fec2f --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/SqrtPriceMathTest.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/SqrtPriceMath.sol'; + +contract SqrtPriceMathTest { + function getNextSqrtPriceFromInput( + uint160 sqrtP, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) external pure returns (uint160 sqrtQ) { + return SqrtPriceMath.getNextSqrtPriceFromInput(sqrtP, liquidity, amountIn, zeroForOne); + } + + function getGasCostOfGetNextSqrtPriceFromInput( + uint160 sqrtP, + uint128 liquidity, + uint256 amountIn, + bool zeroForOne + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + SqrtPriceMath.getNextSqrtPriceFromInput(sqrtP, liquidity, amountIn, zeroForOne); + return gasBefore - gasleft(); + } + + function getNextSqrtPriceFromOutput( + uint160 sqrtP, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) external pure returns (uint160 sqrtQ) { + return SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtP, liquidity, amountOut, zeroForOne); + } + + function getGasCostOfGetNextSqrtPriceFromOutput( + uint160 sqrtP, + uint128 liquidity, + uint256 amountOut, + bool zeroForOne + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtP, liquidity, amountOut, zeroForOne); + return gasBefore - gasleft(); + } + + function getAmount0Delta( + uint160 sqrtLower, + uint160 sqrtUpper, + uint128 liquidity, + bool roundUp + ) external pure returns (uint256 amount0) { + return SqrtPriceMath.getAmount0Delta(sqrtLower, sqrtUpper, liquidity, roundUp); + } + + function getAmount1Delta( + uint160 sqrtLower, + uint160 sqrtUpper, + uint128 liquidity, + bool roundUp + ) external pure returns (uint256 amount1) { + return SqrtPriceMath.getAmount1Delta(sqrtLower, sqrtUpper, liquidity, roundUp); + } + + function getGasCostOfGetAmount0Delta( + uint160 sqrtLower, + uint160 sqrtUpper, + uint128 liquidity, + bool roundUp + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + SqrtPriceMath.getAmount0Delta(sqrtLower, sqrtUpper, liquidity, roundUp); + return gasBefore - gasleft(); + } + + function getGasCostOfGetAmount1Delta( + uint160 sqrtLower, + uint160 sqrtUpper, + uint128 liquidity, + bool roundUp + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + SqrtPriceMath.getAmount1Delta(sqrtLower, sqrtUpper, liquidity, roundUp); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/SwapMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/SwapMathEchidnaTest.sol new file mode 100644 index 0000000..0be6b0b --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/SwapMathEchidnaTest.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/SwapMath.sol'; + +contract SwapMathEchidnaTest { + function checkComputeSwapStepInvariants( + uint160 sqrtPriceRaw, + uint160 sqrtPriceTargetRaw, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) external pure { + require(sqrtPriceRaw > 0); + require(sqrtPriceTargetRaw > 0); + require(feePips > 0); + require(feePips < 1e6); + + (uint160 sqrtQ, uint256 amountIn, uint256 amountOut, uint256 feeAmount) = SwapMath.computeSwapStep( + sqrtPriceRaw, + sqrtPriceTargetRaw, + liquidity, + amountRemaining, + feePips + ); + + assert(amountIn <= type(uint256).max - feeAmount); + + if (amountRemaining < 0) { + assert(amountOut <= uint256(-amountRemaining)); + } else { + assert(amountIn + feeAmount <= uint256(amountRemaining)); + } + + if (sqrtPriceRaw == sqrtPriceTargetRaw) { + assert(amountIn == 0); + assert(amountOut == 0); + assert(feeAmount == 0); + assert(sqrtQ == sqrtPriceTargetRaw); + } + + // didn't reach price target, entire amount must be consumed + if (sqrtQ != sqrtPriceTargetRaw) { + if (amountRemaining < 0) assert(amountOut == uint256(-amountRemaining)); + else assert(amountIn + feeAmount == uint256(amountRemaining)); + } + + // next price is between price and price target + if (sqrtPriceTargetRaw <= sqrtPriceRaw) { + assert(sqrtQ <= sqrtPriceRaw); + assert(sqrtQ >= sqrtPriceTargetRaw); + } else { + assert(sqrtQ >= sqrtPriceRaw); + assert(sqrtQ <= sqrtPriceTargetRaw); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/SwapMathTest.sol b/lib/uniswap/v3-core/contracts/test/SwapMathTest.sol new file mode 100644 index 0000000..b0f59df --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/SwapMathTest.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/SwapMath.sol'; + +contract SwapMathTest { + function computeSwapStep( + uint160 sqrtP, + uint160 sqrtPTarget, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) + external + pure + returns ( + uint160 sqrtQ, + uint256 amountIn, + uint256 amountOut, + uint256 feeAmount + ) + { + return SwapMath.computeSwapStep(sqrtP, sqrtPTarget, liquidity, amountRemaining, feePips); + } + + function getGasCostOfComputeSwapStep( + uint160 sqrtP, + uint160 sqrtPTarget, + uint128 liquidity, + int256 amountRemaining, + uint24 feePips + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + SwapMath.computeSwapStep(sqrtP, sqrtPTarget, liquidity, amountRemaining, feePips); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TestERC20.sol b/lib/uniswap/v3-core/contracts/test/TestERC20.sol new file mode 100644 index 0000000..411e0ea --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TestERC20.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/IERC20Minimal.sol'; + +contract TestERC20 is IERC20Minimal { + mapping(address => uint256) public override balanceOf; + mapping(address => mapping(address => uint256)) public override allowance; + + constructor(uint256 amountToMint) { + mint(msg.sender, amountToMint); + } + + function mint(address to, uint256 amount) public { + uint256 balanceNext = balanceOf[to] + amount; + require(balanceNext >= amount, 'overflow balance'); + balanceOf[to] = balanceNext; + } + + function transfer(address recipient, uint256 amount) external override returns (bool) { + uint256 balanceBefore = balanceOf[msg.sender]; + require(balanceBefore >= amount, 'insufficient balance'); + balanceOf[msg.sender] = balanceBefore - amount; + + uint256 balanceRecipient = balanceOf[recipient]; + require(balanceRecipient + amount >= balanceRecipient, 'recipient balance overflow'); + balanceOf[recipient] = balanceRecipient + amount; + + emit Transfer(msg.sender, recipient, amount); + return true; + } + + function approve(address spender, uint256 amount) external override returns (bool) { + allowance[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external override returns (bool) { + uint256 allowanceBefore = allowance[sender][msg.sender]; + require(allowanceBefore >= amount, 'allowance insufficient'); + + allowance[sender][msg.sender] = allowanceBefore - amount; + + uint256 balanceRecipient = balanceOf[recipient]; + require(balanceRecipient + amount >= balanceRecipient, 'overflow balance recipient'); + balanceOf[recipient] = balanceRecipient + amount; + uint256 balanceSender = balanceOf[sender]; + require(balanceSender >= amount, 'underflow balance sender'); + balanceOf[sender] = balanceSender - amount; + + emit Transfer(sender, recipient, amount); + return true; + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TestUniswapV3Callee.sol b/lib/uniswap/v3-core/contracts/test/TestUniswapV3Callee.sol new file mode 100644 index 0000000..b569935 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TestUniswapV3Callee.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/IERC20Minimal.sol'; + +import '../libraries/SafeCast.sol'; +import '../libraries/TickMath.sol'; + +import '../interfaces/callback/IUniswapV3MintCallback.sol'; +import '../interfaces/callback/IUniswapV3SwapCallback.sol'; +import '../interfaces/callback/IUniswapV3FlashCallback.sol'; + +import '../interfaces/IUniswapV3Pool.sol'; + +contract TestUniswapV3Callee is IUniswapV3MintCallback, IUniswapV3SwapCallback, IUniswapV3FlashCallback { + using SafeCast for uint256; + + function swapExact0For1( + address pool, + uint256 amount0In, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, true, amount0In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swap0ForExact1( + address pool, + uint256 amount1Out, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, true, -amount1Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swapExact1For0( + address pool, + uint256 amount1In, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, false, amount1In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swap1ForExact0( + address pool, + uint256 amount0Out, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, false, -amount0Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swapToLowerSqrtPrice( + address pool, + uint160 sqrtPriceX96, + address recipient + ) external { + IUniswapV3Pool(pool).swap(recipient, true, type(int256).max, sqrtPriceX96, abi.encode(msg.sender)); + } + + function swapToHigherSqrtPrice( + address pool, + uint160 sqrtPriceX96, + address recipient + ) external { + IUniswapV3Pool(pool).swap(recipient, false, type(int256).max, sqrtPriceX96, abi.encode(msg.sender)); + } + + event SwapCallback(int256 amount0Delta, int256 amount1Delta); + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external override { + address sender = abi.decode(data, (address)); + + emit SwapCallback(amount0Delta, amount1Delta); + + if (amount0Delta > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); + } else if (amount1Delta > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); + } else { + // if both are not gt 0, both must be 0. + assert(amount0Delta == 0 && amount1Delta == 0); + } + } + + function mint( + address pool, + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external { + IUniswapV3Pool(pool).mint(recipient, tickLower, tickUpper, amount, abi.encode(msg.sender)); + } + + event MintCallback(uint256 amount0Owed, uint256 amount1Owed); + + function uniswapV3MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external override { + address sender = abi.decode(data, (address)); + + emit MintCallback(amount0Owed, amount1Owed); + if (amount0Owed > 0) + IERC20Minimal(IUniswapV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, amount0Owed); + if (amount1Owed > 0) + IERC20Minimal(IUniswapV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, amount1Owed); + } + + event FlashCallback(uint256 fee0, uint256 fee1); + + function flash( + address pool, + address recipient, + uint256 amount0, + uint256 amount1, + uint256 pay0, + uint256 pay1 + ) external { + IUniswapV3Pool(pool).flash(recipient, amount0, amount1, abi.encode(msg.sender, pay0, pay1)); + } + + function uniswapV3FlashCallback( + uint256 fee0, + uint256 fee1, + bytes calldata data + ) external override { + emit FlashCallback(fee0, fee1); + + (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); + + if (pay0 > 0) IERC20Minimal(IUniswapV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, pay0); + if (pay1 > 0) IERC20Minimal(IUniswapV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, pay1); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TestUniswapV3ReentrantCallee.sol b/lib/uniswap/v3-core/contracts/test/TestUniswapV3ReentrantCallee.sol new file mode 100644 index 0000000..fbd0b2a --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TestUniswapV3ReentrantCallee.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/TickMath.sol'; + +import '../interfaces/callback/IUniswapV3SwapCallback.sol'; + +import '../interfaces/IUniswapV3Pool.sol'; + +contract TestUniswapV3ReentrantCallee is IUniswapV3SwapCallback { + string private constant expectedReason = 'LOK'; + + function swapToReenter(address pool) external { + IUniswapV3Pool(pool).swap(address(0), false, 1, TickMath.MAX_SQRT_RATIO - 1, new bytes(0)); + } + + function uniswapV3SwapCallback( + int256, + int256, + bytes calldata + ) external override { + // try to reenter swap + try IUniswapV3Pool(msg.sender).swap(address(0), false, 1, 0, new bytes(0)) {} catch Error( + string memory reason + ) { + require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); + } + + // try to reenter mint + try IUniswapV3Pool(msg.sender).mint(address(0), 0, 0, 0, new bytes(0)) {} catch Error(string memory reason) { + require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); + } + + // try to reenter collect + try IUniswapV3Pool(msg.sender).collect(address(0), 0, 0, 0, 0) {} catch Error(string memory reason) { + require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); + } + + // try to reenter burn + try IUniswapV3Pool(msg.sender).burn(0, 0, 0) {} catch Error(string memory reason) { + require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); + } + + // try to reenter flash + try IUniswapV3Pool(msg.sender).flash(address(0), 0, 0, new bytes(0)) {} catch Error(string memory reason) { + require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); + } + + // try to reenter collectProtocol + try IUniswapV3Pool(msg.sender).collectProtocol(address(0), 0, 0) {} catch Error(string memory reason) { + require(keccak256(abi.encode(reason)) == keccak256(abi.encode(expectedReason))); + } + + require(false, 'Unable to reenter'); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TestUniswapV3Router.sol b/lib/uniswap/v3-core/contracts/test/TestUniswapV3Router.sol new file mode 100644 index 0000000..60f2e3f --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TestUniswapV3Router.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/SafeCast.sol'; +import '../libraries/TickMath.sol'; + +import '../interfaces/IERC20Minimal.sol'; +import '../interfaces/callback/IUniswapV3SwapCallback.sol'; +import '../interfaces/IUniswapV3Pool.sol'; + +contract TestUniswapV3Router is IUniswapV3SwapCallback { + using SafeCast for uint256; + + // flash swaps for an exact amount of token0 in the output pool + function swapForExact0Multi( + address recipient, + address poolInput, + address poolOutput, + uint256 amount0Out + ) external { + address[] memory pools = new address[](1); + pools[0] = poolInput; + IUniswapV3Pool(poolOutput).swap( + recipient, + false, + -amount0Out.toInt256(), + TickMath.MAX_SQRT_RATIO - 1, + abi.encode(pools, msg.sender) + ); + } + + // flash swaps for an exact amount of token1 in the output pool + function swapForExact1Multi( + address recipient, + address poolInput, + address poolOutput, + uint256 amount1Out + ) external { + address[] memory pools = new address[](1); + pools[0] = poolInput; + IUniswapV3Pool(poolOutput).swap( + recipient, + true, + -amount1Out.toInt256(), + TickMath.MIN_SQRT_RATIO + 1, + abi.encode(pools, msg.sender) + ); + } + + event SwapCallback(int256 amount0Delta, int256 amount1Delta); + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) public override { + emit SwapCallback(amount0Delta, amount1Delta); + + (address[] memory pools, address payer) = abi.decode(data, (address[], address)); + + if (pools.length == 1) { + // get the address and amount of the token that we need to pay + address tokenToBePaid = amount0Delta > 0 + ? IUniswapV3Pool(msg.sender).token0() + : IUniswapV3Pool(msg.sender).token1(); + int256 amountToBePaid = amount0Delta > 0 ? amount0Delta : amount1Delta; + + bool zeroForOne = tokenToBePaid == IUniswapV3Pool(pools[0]).token1(); + IUniswapV3Pool(pools[0]).swap( + msg.sender, + zeroForOne, + -amountToBePaid, + zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1, + abi.encode(new address[](0), payer) + ); + } else { + if (amount0Delta > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token0()).transferFrom( + payer, + msg.sender, + uint256(amount0Delta) + ); + } else { + IERC20Minimal(IUniswapV3Pool(msg.sender).token1()).transferFrom( + payer, + msg.sender, + uint256(amount1Delta) + ); + } + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TestUniswapV3SwapPay.sol b/lib/uniswap/v3-core/contracts/test/TestUniswapV3SwapPay.sol new file mode 100644 index 0000000..0a78c88 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TestUniswapV3SwapPay.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/IERC20Minimal.sol'; + +import '../interfaces/callback/IUniswapV3SwapCallback.sol'; +import '../interfaces/IUniswapV3Pool.sol'; + +contract TestUniswapV3SwapPay is IUniswapV3SwapCallback { + function swap( + address pool, + address recipient, + bool zeroForOne, + uint160 sqrtPriceX96, + int256 amountSpecified, + uint256 pay0, + uint256 pay1 + ) external { + IUniswapV3Pool(pool).swap( + recipient, + zeroForOne, + amountSpecified, + sqrtPriceX96, + abi.encode(msg.sender, pay0, pay1) + ); + } + + function uniswapV3SwapCallback( + int256, + int256, + bytes calldata data + ) external override { + (address sender, uint256 pay0, uint256 pay1) = abi.decode(data, (address, uint256, uint256)); + + if (pay0 > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(pay0)); + } else if (pay1 > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(pay1)); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickBitmapEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/TickBitmapEchidnaTest.sol new file mode 100644 index 0000000..675798a --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickBitmapEchidnaTest.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/TickBitmap.sol'; + +contract TickBitmapEchidnaTest { + using TickBitmap for mapping(int16 => uint256); + + mapping(int16 => uint256) private bitmap; + + // returns whether the given tick is initialized + function isInitialized(int24 tick) private view returns (bool) { + (int24 next, bool initialized) = bitmap.nextInitializedTickWithinOneWord(tick, 1, true); + return next == tick ? initialized : false; + } + + function flipTick(int24 tick) external { + bool before = isInitialized(tick); + bitmap.flipTick(tick, 1); + assert(isInitialized(tick) == !before); + } + + function checkNextInitializedTickWithinOneWordInvariants(int24 tick, bool lte) external view { + (int24 next, bool initialized) = bitmap.nextInitializedTickWithinOneWord(tick, 1, lte); + if (lte) { + // type(int24).min + 256 + require(tick >= -8388352); + assert(next <= tick); + assert(tick - next < 256); + // all the ticks between the input tick and the next tick should be uninitialized + for (int24 i = tick; i > next; i--) { + assert(!isInitialized(i)); + } + assert(isInitialized(next) == initialized); + } else { + // type(int24).max - 256 + require(tick < 8388351); + assert(next > tick); + assert(next - tick <= 256); + // all the ticks between the input tick and the next tick should be uninitialized + for (int24 i = tick + 1; i < next; i++) { + assert(!isInitialized(i)); + } + assert(isInitialized(next) == initialized); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickBitmapTest.sol b/lib/uniswap/v3-core/contracts/test/TickBitmapTest.sol new file mode 100644 index 0000000..c6fb6b0 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickBitmapTest.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/TickBitmap.sol'; + +contract TickBitmapTest { + using TickBitmap for mapping(int16 => uint256); + + mapping(int16 => uint256) public bitmap; + + function flipTick(int24 tick) external { + bitmap.flipTick(tick, 1); + } + + function getGasCostOfFlipTick(int24 tick) external returns (uint256) { + uint256 gasBefore = gasleft(); + bitmap.flipTick(tick, 1); + return gasBefore - gasleft(); + } + + function nextInitializedTickWithinOneWord(int24 tick, bool lte) + external + view + returns (int24 next, bool initialized) + { + return bitmap.nextInitializedTickWithinOneWord(tick, 1, lte); + } + + function getGasCostOfNextInitializedTickWithinOneWord(int24 tick, bool lte) external view returns (uint256) { + uint256 gasBefore = gasleft(); + bitmap.nextInitializedTickWithinOneWord(tick, 1, lte); + return gasBefore - gasleft(); + } + + // returns whether the given tick is initialized + function isInitialized(int24 tick) external view returns (bool) { + (int24 next, bool initialized) = bitmap.nextInitializedTickWithinOneWord(tick, 1, true); + return next == tick ? initialized : false; + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/TickEchidnaTest.sol new file mode 100644 index 0000000..1d5514f --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickEchidnaTest.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/Tick.sol'; + +contract TickEchidnaTest { + function checkTickSpacingToParametersInvariants(int24 tickSpacing) external pure { + require(tickSpacing <= TickMath.MAX_TICK); + require(tickSpacing > 0); + + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + + uint128 maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(tickSpacing); + + // symmetry around 0 tick + assert(maxTick == -minTick); + // positive max tick + assert(maxTick > 0); + // divisibility + assert((maxTick - minTick) % tickSpacing == 0); + + uint256 numTicks = uint256((maxTick - minTick) / tickSpacing) + 1; + // max liquidity at every tick is less than the cap + assert(uint256(maxLiquidityPerTick) * numTicks <= type(uint128).max); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/TickMathEchidnaTest.sol new file mode 100644 index 0000000..91064b0 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickMathEchidnaTest.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/TickMath.sol'; + +contract TickMathEchidnaTest { + // uniqueness and increasing order + function checkGetSqrtRatioAtTickInvariants(int24 tick) external pure { + uint160 ratio = TickMath.getSqrtRatioAtTick(tick); + assert(TickMath.getSqrtRatioAtTick(tick - 1) < ratio && ratio < TickMath.getSqrtRatioAtTick(tick + 1)); + assert(ratio >= TickMath.MIN_SQRT_RATIO); + assert(ratio <= TickMath.MAX_SQRT_RATIO); + } + + // the ratio is always between the returned tick and the returned tick+1 + function checkGetTickAtSqrtRatioInvariants(uint160 ratio) external pure { + int24 tick = TickMath.getTickAtSqrtRatio(ratio); + assert(ratio >= TickMath.getSqrtRatioAtTick(tick) && ratio < TickMath.getSqrtRatioAtTick(tick + 1)); + assert(tick >= TickMath.MIN_TICK); + assert(tick < TickMath.MAX_TICK); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickMathTest.sol b/lib/uniswap/v3-core/contracts/test/TickMathTest.sol new file mode 100644 index 0000000..2816e57 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickMathTest.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/TickMath.sol'; + +contract TickMathTest { + function getSqrtRatioAtTick(int24 tick) external pure returns (uint160) { + return TickMath.getSqrtRatioAtTick(tick); + } + + function getGasCostOfGetSqrtRatioAtTick(int24 tick) external view returns (uint256) { + uint256 gasBefore = gasleft(); + TickMath.getSqrtRatioAtTick(tick); + return gasBefore - gasleft(); + } + + function getTickAtSqrtRatio(uint160 sqrtPriceX96) external pure returns (int24) { + return TickMath.getTickAtSqrtRatio(sqrtPriceX96); + } + + function getGasCostOfGetTickAtSqrtRatio(uint160 sqrtPriceX96) external view returns (uint256) { + uint256 gasBefore = gasleft(); + TickMath.getTickAtSqrtRatio(sqrtPriceX96); + return gasBefore - gasleft(); + } + + function MIN_SQRT_RATIO() external pure returns (uint160) { + return TickMath.MIN_SQRT_RATIO; + } + + function MAX_SQRT_RATIO() external pure returns (uint160) { + return TickMath.MAX_SQRT_RATIO; + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickOverflowSafetyEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/TickOverflowSafetyEchidnaTest.sol new file mode 100644 index 0000000..f1b8ec1 --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickOverflowSafetyEchidnaTest.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/Tick.sol'; + +contract TickOverflowSafetyEchidnaTest { + using Tick for mapping(int24 => Tick.Info); + + int24 private constant MIN_TICK = -16; + int24 private constant MAX_TICK = 16; + uint128 private constant MAX_LIQUIDITY = type(uint128).max / 32; + + mapping(int24 => Tick.Info) private ticks; + int24 private tick = 0; + + // used to track how much total liquidity has been added. should never be negative + int256 totalLiquidity = 0; + // half the cap of fee growth has happened, this can overflow + uint256 private feeGrowthGlobal0X128 = type(uint256).max / 2; + uint256 private feeGrowthGlobal1X128 = type(uint256).max / 2; + // how much total growth has happened, this cannot overflow + uint256 private totalGrowth0 = 0; + uint256 private totalGrowth1 = 0; + + function increaseFeeGrowthGlobal0X128(uint256 amount) external { + require(totalGrowth0 + amount > totalGrowth0); // overflow check + feeGrowthGlobal0X128 += amount; // overflow desired + totalGrowth0 += amount; + } + + function increaseFeeGrowthGlobal1X128(uint256 amount) external { + require(totalGrowth1 + amount > totalGrowth1); // overflow check + feeGrowthGlobal1X128 += amount; // overflow desired + totalGrowth1 += amount; + } + + function setPosition( + int24 tickLower, + int24 tickUpper, + int128 liquidityDelta + ) external { + require(tickLower > MIN_TICK); + require(tickUpper < MAX_TICK); + require(tickLower < tickUpper); + bool flippedLower = ticks.update( + tickLower, + tick, + liquidityDelta, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, + 0, + 0, + uint32(block.timestamp), + false, + MAX_LIQUIDITY + ); + bool flippedUpper = ticks.update( + tickUpper, + tick, + liquidityDelta, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, + 0, + 0, + uint32(block.timestamp), + true, + MAX_LIQUIDITY + ); + + if (flippedLower) { + if (liquidityDelta < 0) { + assert(ticks[tickLower].liquidityGross == 0); + ticks.clear(tickLower); + } else assert(ticks[tickLower].liquidityGross > 0); + } + + if (flippedUpper) { + if (liquidityDelta < 0) { + assert(ticks[tickUpper].liquidityGross == 0); + ticks.clear(tickUpper); + } else assert(ticks[tickUpper].liquidityGross > 0); + } + + totalLiquidity += liquidityDelta; + // requires should have prevented this + assert(totalLiquidity >= 0); + + if (totalLiquidity == 0) { + totalGrowth0 = 0; + totalGrowth1 = 0; + } + } + + function moveToTick(int24 target) external { + require(target > MIN_TICK); + require(target < MAX_TICK); + while (tick != target) { + if (tick < target) { + if (ticks[tick + 1].liquidityGross > 0) + ticks.cross(tick + 1, feeGrowthGlobal0X128, feeGrowthGlobal1X128, 0, 0, uint32(block.timestamp)); + tick++; + } else { + if (ticks[tick].liquidityGross > 0) + ticks.cross(tick, feeGrowthGlobal0X128, feeGrowthGlobal1X128, 0, 0, uint32(block.timestamp)); + tick--; + } + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/TickTest.sol b/lib/uniswap/v3-core/contracts/test/TickTest.sol new file mode 100644 index 0000000..d9fa7cb --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/TickTest.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../libraries/Tick.sol'; + +contract TickTest { + using Tick for mapping(int24 => Tick.Info); + + mapping(int24 => Tick.Info) public ticks; + + function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) external pure returns (uint128) { + return Tick.tickSpacingToMaxLiquidityPerTick(tickSpacing); + } + + function setTick(int24 tick, Tick.Info memory info) external { + ticks[tick] = info; + } + + function getFeeGrowthInside( + int24 tickLower, + int24 tickUpper, + int24 tickCurrent, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128 + ) external view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + return ticks.getFeeGrowthInside(tickLower, tickUpper, tickCurrent, feeGrowthGlobal0X128, feeGrowthGlobal1X128); + } + + function update( + int24 tick, + int24 tickCurrent, + int128 liquidityDelta, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time, + bool upper, + uint128 maxLiquidity + ) external returns (bool flipped) { + return + ticks.update( + tick, + tickCurrent, + liquidityDelta, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, + secondsPerLiquidityCumulativeX128, + tickCumulative, + time, + upper, + maxLiquidity + ); + } + + function clear(int24 tick) external { + ticks.clear(tick); + } + + function cross( + int24 tick, + uint256 feeGrowthGlobal0X128, + uint256 feeGrowthGlobal1X128, + uint160 secondsPerLiquidityCumulativeX128, + int56 tickCumulative, + uint32 time + ) external returns (int128 liquidityNet) { + return + ticks.cross( + tick, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, + secondsPerLiquidityCumulativeX128, + tickCumulative, + time + ); + } +} diff --git a/lib/uniswap/v3-core/contracts/test/UniswapV3PoolSwapTest.sol b/lib/uniswap/v3-core/contracts/test/UniswapV3PoolSwapTest.sol new file mode 100644 index 0000000..d1f900b --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/UniswapV3PoolSwapTest.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/IERC20Minimal.sol'; + +import '../interfaces/callback/IUniswapV3SwapCallback.sol'; +import '../interfaces/IUniswapV3Pool.sol'; + +contract UniswapV3PoolSwapTest is IUniswapV3SwapCallback { + int256 private _amount0Delta; + int256 private _amount1Delta; + + function getSwapResult( + address pool, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96 + ) + external + returns ( + int256 amount0Delta, + int256 amount1Delta, + uint160 nextSqrtRatio + ) + { + (amount0Delta, amount1Delta) = IUniswapV3Pool(pool).swap( + address(0), + zeroForOne, + amountSpecified, + sqrtPriceLimitX96, + abi.encode(msg.sender) + ); + + (nextSqrtRatio, , , , , , ) = IUniswapV3Pool(pool).slot0(); + } + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external override { + address sender = abi.decode(data, (address)); + + if (amount0Delta > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); + } else if (amount1Delta > 0) { + IERC20Minimal(IUniswapV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); + } + } +} diff --git a/lib/uniswap/v3-core/contracts/test/UnsafeMathEchidnaTest.sol b/lib/uniswap/v3-core/contracts/test/UnsafeMathEchidnaTest.sol new file mode 100644 index 0000000..0a44d7e --- /dev/null +++ b/lib/uniswap/v3-core/contracts/test/UnsafeMathEchidnaTest.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/UnsafeMath.sol'; + +contract UnsafeMathEchidnaTest { + function checkDivRoundingUp(uint256 x, uint256 d) external pure { + require(d > 0); + uint256 z = UnsafeMath.divRoundingUp(x, d); + uint256 diff = z - (x / d); + if (x % d == 0) { + assert(diff == 0); + } else { + assert(diff == 1); + } + } +} diff --git a/lib/uniswap/v3-core/echidna.config.yml b/lib/uniswap/v3-core/echidna.config.yml new file mode 100644 index 0000000..64a761d --- /dev/null +++ b/lib/uniswap/v3-core/echidna.config.yml @@ -0,0 +1,74 @@ +#format can be "text" or "json" for different output (human or machine readable) +format: 'text' +#checkAsserts checks assertions +checkAsserts: true +#coverage controls coverage guided testing +coverage: false +# #psender is the sender for property transactions; by default intentionally +# #the same as contract deployer +# psender: "0x00a329c0648769a73afac7f9381e08fb43dbea70" +# #prefix is the prefix for Boolean functions that are properties to be checked +# prefix: "echidna_" +# #propMaxGas defines gas cost at which a property fails +# propMaxGas: 8000030 +# #testMaxGas is a gas limit; does not cause failure, but terminates sequence +# testMaxGas: 8000030 +# #maxGasprice is the maximum gas price +# maxGasprice: 100000000000 +# #testLimit is the number of test sequences to run +# testLimit: 50000 +# #stopOnFail makes echidna terminate as soon as any property fails and has been shrunk +# stopOnFail: false +# #estimateGas makes echidna perform analysis of maximum gas costs for functions (experimental) +# estimateGas: false +# #seqLen defines how many transactions are in a test sequence +# seqLen: 100 +# #shrinkLimit determines how much effort is spent shrinking failing sequences +# shrinkLimit: 5000 +# #contractAddr is the address of the contract itself +# contractAddr: "0x00a329c0648769a73afac7f9381e08fb43dbea72" +# #deployer is address of the contract deployer (who often is privileged owner, etc.) +# deployer: "0x00a329c0648769a73afac7f9381e08fb43dbea70" +# #sender is set of addresses transactions may originate from +# sender: ["0x10000", "0x20000", "0x00a329c0648769a73afac7f9381e08fb43dbea70"] +# #balanceAddr is default balance for addresses +# balanceAddr: 0xffffffff +# #balanceContract overrides balanceAddr for the contract address +# balanceContract: 0 +# #solcArgs allows special args to solc +# solcArgs: "" +# #solcLibs is solc libraries +# solcLibs: [] +# #cryticArgs allows special args to crytic +# cryticArgs: [] +# #quiet produces (much) less verbose output +# quiet: false +# #initialize the blockchain with some data +# initialize: null +# #whether ot not to use the multi-abi mode of testing +# multi-abi: false +# #benchmarkMode enables benchmark mode +# benchmarkMode: false +# #timeout controls test timeout settings +# timeout: null +# #seed not defined by default, is the random seed +# #seed: 0 +# #dictFreq controls how often to use echidna's internal dictionary vs random +# #values +# dictFreq: 0.40 +# maxTimeDelay: 604800 +# #maximum time between generated txs; default is one week +# maxBlockDelay: 60480 +# #maximum number of blocks elapsed between generated txs; default is expected increment in one week +# # timeout: +# #campaign timeout (in seconds) +# # list of methods to filter +# filterFunctions: [] +# # by default, blacklist methods in filterFunctions +# filterBlacklist: true +# #directory to save the corpus; by default is disabled +# corpusDir: null +# # constants for corpus mutations (for experimentation only) +# mutConsts: [100, 1, 1] +# # maximum value to send to payable functions +# maxValue: 100000000000000000000 # 100 eth diff --git a/lib/uniswap/v3-core/hardhat.config.ts b/lib/uniswap/v3-core/hardhat.config.ts new file mode 100644 index 0000000..898ff23 --- /dev/null +++ b/lib/uniswap/v3-core/hardhat.config.ts @@ -0,0 +1,65 @@ +import 'hardhat-typechain' +import '@nomiclabs/hardhat-ethers' +import '@nomiclabs/hardhat-waffle' +import '@nomiclabs/hardhat-etherscan' + +export default { + networks: { + hardhat: { + allowUnlimitedContractSize: false, + }, + mainnet: { + url: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + ropsten: { + url: `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + rinkeby: { + url: `https://rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + goerli: { + url: `https://goerli.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + kovan: { + url: `https://kovan.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + arbitrumRinkeby: { + url: `https://arbitrum-rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + arbitrum: { + url: `https://arbitrum-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + optimismKovan: { + url: `https://optimism-kovan.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + optimism: { + url: `https://optimism-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + mumbai: { + url: `https://polygon-mumbai.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + polygon: { + url: `https://polygon-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + }, + etherscan: { + // Your API key for Etherscan + // Obtain one at https://etherscan.io/ + apiKey: process.env.ETHERSCAN_API_KEY, + }, + solidity: { + version: '0.7.6', + settings: { + optimizer: { + enabled: true, + runs: 800, + }, + metadata: { + // do not include the metadata hash, since this is machine dependent + // and we want all generated code to be deterministic + // https://docs.soliditylang.org/en/v0.7.6/metadata.html + bytecodeHash: 'none', + }, + }, + }, +} diff --git a/lib/uniswap/v3-core/package.json b/lib/uniswap/v3-core/package.json new file mode 100644 index 0000000..188721b --- /dev/null +++ b/lib/uniswap/v3-core/package.json @@ -0,0 +1,59 @@ +{ + "name": "@uniswap/v3-core", + "description": "🦄 Core smart contracts of Uniswap V3", + "license": "BUSL-1.1", + "publishConfig": { + "access": "public" + }, + "version": "1.0.1", + "homepage": "https://uniswap.org", + "keywords": [ + "uniswap", + "core", + "v3" + ], + "repository": { + "type": "git", + "url": "https://github.com/Uniswap/uniswap-v3-core" + }, + "files": [ + "contracts/interfaces", + "contracts/libraries", + "artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json", + "artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json", + "artifacts/contracts/interfaces/**/*.json", + "!artifacts/contracts/interfaces/**/*.dbg.json" + ], + "engines": { + "node": ">=10" + }, + "dependencies": {}, + "devDependencies": { + "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-etherscan": "^2.1.8", + "@nomiclabs/hardhat-waffle": "^2.0.1", + "@typechain/ethers-v5": "^4.0.0", + "@types/chai": "^4.2.6", + "@types/mocha": "^5.2.7", + "chai": "^4.2.0", + "decimal.js": "^10.2.1", + "ethereum-waffle": "^3.0.2", + "ethers": "^5.0.8", + "hardhat": "^2.2.0", + "hardhat-typechain": "^0.3.5", + "mocha": "^6.2.2", + "mocha-chai-jest-snapshot": "^1.1.0", + "prettier": "^2.0.5", + "prettier-plugin-solidity": "^1.0.0-alpha.59", + "solhint": "^3.2.1", + "solhint-plugin-prettier": "^0.0.5", + "ts-generator": "^0.1.1", + "ts-node": "^8.5.4", + "typechain": "^4.0.0", + "typescript": "^3.7.3" + }, + "scripts": { + "compile": "hardhat compile", + "test": "hardhat test" + } +} diff --git a/lib/uniswap/v3-core/test/BitMath.spec.ts b/lib/uniswap/v3-core/test/BitMath.spec.ts new file mode 100644 index 0000000..00b9285 --- /dev/null +++ b/lib/uniswap/v3-core/test/BitMath.spec.ts @@ -0,0 +1,79 @@ +import { expect } from './shared/expect' +import { BitMathTest } from '../typechain/BitMathTest' +import { ethers, waffle } from 'hardhat' +import snapshotGasCost from './shared/snapshotGasCost' + +const { BigNumber } = ethers + +describe('BitMath', () => { + let bitMath: BitMathTest + const fixture = async () => { + const factory = await ethers.getContractFactory('BitMathTest') + return (await factory.deploy()) as BitMathTest + } + beforeEach('deploy BitMathTest', async () => { + bitMath = await waffle.loadFixture(fixture) + }) + + describe('#mostSignificantBit', () => { + it('0', async () => { + await expect(bitMath.mostSignificantBit(0)).to.be.reverted + }) + it('1', async () => { + expect(await bitMath.mostSignificantBit(1)).to.eq(0) + }) + it('2', async () => { + expect(await bitMath.mostSignificantBit(2)).to.eq(1) + }) + it('all powers of 2', async () => { + const results = await Promise.all( + [...Array(255)].map((_, i) => bitMath.mostSignificantBit(BigNumber.from(2).pow(i))) + ) + expect(results).to.deep.eq([...Array(255)].map((_, i) => i)) + }) + it('uint256(-1)', async () => { + expect(await bitMath.mostSignificantBit(BigNumber.from(2).pow(256).sub(1))).to.eq(255) + }) + + it('gas cost of smaller number', async () => { + await snapshotGasCost(bitMath.getGasCostOfMostSignificantBit(BigNumber.from(3568))) + }) + it('gas cost of max uint128', async () => { + await snapshotGasCost(bitMath.getGasCostOfMostSignificantBit(BigNumber.from(2).pow(128).sub(1))) + }) + it('gas cost of max uint256', async () => { + await snapshotGasCost(bitMath.getGasCostOfMostSignificantBit(BigNumber.from(2).pow(256).sub(1))) + }) + }) + + describe('#leastSignificantBit', () => { + it('0', async () => { + await expect(bitMath.leastSignificantBit(0)).to.be.reverted + }) + it('1', async () => { + expect(await bitMath.leastSignificantBit(1)).to.eq(0) + }) + it('2', async () => { + expect(await bitMath.leastSignificantBit(2)).to.eq(1) + }) + it('all powers of 2', async () => { + const results = await Promise.all( + [...Array(255)].map((_, i) => bitMath.leastSignificantBit(BigNumber.from(2).pow(i))) + ) + expect(results).to.deep.eq([...Array(255)].map((_, i) => i)) + }) + it('uint256(-1)', async () => { + expect(await bitMath.leastSignificantBit(BigNumber.from(2).pow(256).sub(1))).to.eq(0) + }) + + it('gas cost of smaller number', async () => { + await snapshotGasCost(bitMath.getGasCostOfLeastSignificantBit(BigNumber.from(3568))) + }) + it('gas cost of max uint128', async () => { + await snapshotGasCost(bitMath.getGasCostOfLeastSignificantBit(BigNumber.from(2).pow(128).sub(1))) + }) + it('gas cost of max uint256', async () => { + await snapshotGasCost(bitMath.getGasCostOfLeastSignificantBit(BigNumber.from(2).pow(256).sub(1))) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/FullMath.spec.ts b/lib/uniswap/v3-core/test/FullMath.spec.ts new file mode 100644 index 0000000..a7dfb92 --- /dev/null +++ b/lib/uniswap/v3-core/test/FullMath.spec.ts @@ -0,0 +1,182 @@ +import { ethers } from 'hardhat' +import { FullMathTest } from '../typechain/FullMathTest' +import { expect } from './shared/expect' +import { Decimal } from 'decimal.js' + +const { + BigNumber, + constants: { MaxUint256 }, +} = ethers +const Q128 = BigNumber.from(2).pow(128) + +Decimal.config({ toExpNeg: -500, toExpPos: 500 }) + +describe('FullMath', () => { + let fullMath: FullMathTest + before('deploy FullMathTest', async () => { + const factory = await ethers.getContractFactory('FullMathTest') + fullMath = (await factory.deploy()) as FullMathTest + }) + + describe('#mulDiv', () => { + it('reverts if denominator is 0', async () => { + await expect(fullMath.mulDiv(Q128, 5, 0)).to.be.reverted + }) + it('reverts if denominator is 0 and numerator overflows', async () => { + await expect(fullMath.mulDiv(Q128, Q128, 0)).to.be.reverted + }) + it('reverts if output overflows uint256', async () => { + await expect(fullMath.mulDiv(Q128, Q128, 1)).to.be.reverted + }) + it('reverts if output overflows uint256', async () => { + await expect(fullMath.mulDiv(Q128, Q128, 1)).to.be.reverted + }) + it('reverts on overflow with all max inputs', async () => { + await expect(fullMath.mulDiv(MaxUint256, MaxUint256, MaxUint256.sub(1))).to.be.reverted + }) + + it('all max inputs', async () => { + expect(await fullMath.mulDiv(MaxUint256, MaxUint256, MaxUint256)).to.eq(MaxUint256) + }) + + it('accurate without phantom overflow', async () => { + const result = Q128.div(3) + expect( + await fullMath.mulDiv( + Q128, + /*0.5=*/ BigNumber.from(50).mul(Q128).div(100), + /*1.5=*/ BigNumber.from(150).mul(Q128).div(100) + ) + ).to.eq(result) + }) + + it('accurate with phantom overflow', async () => { + const result = BigNumber.from(4375).mul(Q128).div(1000) + expect(await fullMath.mulDiv(Q128, BigNumber.from(35).mul(Q128), BigNumber.from(8).mul(Q128))).to.eq(result) + }) + + it('accurate with phantom overflow and repeating decimal', async () => { + const result = BigNumber.from(1).mul(Q128).div(3) + expect(await fullMath.mulDiv(Q128, BigNumber.from(1000).mul(Q128), BigNumber.from(3000).mul(Q128))).to.eq(result) + }) + }) + + describe('#mulDivRoundingUp', () => { + it('reverts if denominator is 0', async () => { + await expect(fullMath.mulDivRoundingUp(Q128, 5, 0)).to.be.reverted + }) + it('reverts if denominator is 0 and numerator overflows', async () => { + await expect(fullMath.mulDivRoundingUp(Q128, Q128, 0)).to.be.reverted + }) + it('reverts if output overflows uint256', async () => { + await expect(fullMath.mulDivRoundingUp(Q128, Q128, 1)).to.be.reverted + }) + it('reverts on overflow with all max inputs', async () => { + await expect(fullMath.mulDivRoundingUp(MaxUint256, MaxUint256, MaxUint256.sub(1))).to.be.reverted + }) + + it('reverts if mulDiv overflows 256 bits after rounding up', async () => { + await expect( + fullMath.mulDivRoundingUp( + '535006138814359', + '432862656469423142931042426214547535783388063929571229938474969', + '2' + ) + ).to.be.reverted + }) + + it('reverts if mulDiv overflows 256 bits after rounding up case 2', async () => { + await expect( + fullMath.mulDivRoundingUp( + '115792089237316195423570985008687907853269984659341747863450311749907997002549', + '115792089237316195423570985008687907853269984659341747863450311749907997002550', + '115792089237316195423570985008687907853269984653042931687443039491902864365164' + ) + ).to.be.reverted + }) + + it('all max inputs', async () => { + expect(await fullMath.mulDivRoundingUp(MaxUint256, MaxUint256, MaxUint256)).to.eq(MaxUint256) + }) + + it('accurate without phantom overflow', async () => { + const result = Q128.div(3).add(1) + expect( + await fullMath.mulDivRoundingUp( + Q128, + /*0.5=*/ BigNumber.from(50).mul(Q128).div(100), + /*1.5=*/ BigNumber.from(150).mul(Q128).div(100) + ) + ).to.eq(result) + }) + + it('accurate with phantom overflow', async () => { + const result = BigNumber.from(4375).mul(Q128).div(1000) + expect(await fullMath.mulDivRoundingUp(Q128, BigNumber.from(35).mul(Q128), BigNumber.from(8).mul(Q128))).to.eq( + result + ) + }) + + it('accurate with phantom overflow and repeating decimal', async () => { + const result = BigNumber.from(1).mul(Q128).div(3).add(1) + expect( + await fullMath.mulDivRoundingUp(Q128, BigNumber.from(1000).mul(Q128), BigNumber.from(3000).mul(Q128)) + ).to.eq(result) + }) + }) + + function pseudoRandomBigNumber() { + return BigNumber.from(new Decimal(MaxUint256.toString()).mul(Math.random().toString()).round().toString()) + } + + // tiny fuzzer. unskip to run + it.skip('check a bunch of random inputs against JS implementation', async () => { + // generates random inputs + const tests = Array(1_000) + .fill(null) + .map(() => { + return { + x: pseudoRandomBigNumber(), + y: pseudoRandomBigNumber(), + d: pseudoRandomBigNumber(), + } + }) + .map(({ x, y, d }) => { + return { + input: { + x, + y, + d, + }, + floored: fullMath.mulDiv(x, y, d), + ceiled: fullMath.mulDivRoundingUp(x, y, d), + } + }) + + await Promise.all( + tests.map(async ({ input: { x, y, d }, floored, ceiled }) => { + if (d.eq(0)) { + await expect(floored).to.be.reverted + await expect(ceiled).to.be.reverted + return + } + + if (x.eq(0) || y.eq(0)) { + await expect(floored).to.eq(0) + await expect(ceiled).to.eq(0) + } else if (x.mul(y).div(d).gt(MaxUint256)) { + await expect(floored).to.be.reverted + await expect(ceiled).to.be.reverted + } else { + expect(await floored).to.eq(x.mul(y).div(d)) + expect(await ceiled).to.eq( + x + .mul(y) + .div(d) + .add(x.mul(y).mod(d).gt(0) ? 1 : 0) + ) + } + }) + ) + }) +}) diff --git a/lib/uniswap/v3-core/test/LiquidityMath.spec.ts b/lib/uniswap/v3-core/test/LiquidityMath.spec.ts new file mode 100644 index 0000000..76f6ac2 --- /dev/null +++ b/lib/uniswap/v3-core/test/LiquidityMath.spec.ts @@ -0,0 +1,44 @@ +import { expect } from './shared/expect' +import { LiquidityMathTest } from '../typechain/LiquidityMathTest' +import { ethers, waffle } from 'hardhat' +import snapshotGasCost from './shared/snapshotGasCost' + +const { BigNumber } = ethers + +describe('LiquidityMath', () => { + let liquidityMath: LiquidityMathTest + const fixture = async () => { + const factory = await ethers.getContractFactory('LiquidityMathTest') + return (await factory.deploy()) as LiquidityMathTest + } + beforeEach('deploy LiquidityMathTest', async () => { + liquidityMath = await waffle.loadFixture(fixture) + }) + + describe('#addDelta', () => { + it('1 + 0', async () => { + expect(await liquidityMath.addDelta(1, 0)).to.eq(1) + }) + it('1 + -1', async () => { + expect(await liquidityMath.addDelta(1, -1)).to.eq(0) + }) + it('1 + 1', async () => { + expect(await liquidityMath.addDelta(1, 1)).to.eq(2) + }) + it('2**128-15 + 15 overflows', async () => { + await expect(liquidityMath.addDelta(BigNumber.from(2).pow(128).sub(15), 15)).to.be.revertedWith('LA') + }) + it('0 + -1 underflows', async () => { + await expect(liquidityMath.addDelta(0, -1)).to.be.revertedWith('LS') + }) + it('3 + -4 underflows', async () => { + await expect(liquidityMath.addDelta(3, -4)).to.be.revertedWith('LS') + }) + it('gas add', async () => { + await snapshotGasCost(liquidityMath.getGasCostOfAddDelta(15, 4)) + }) + it('gas sub', async () => { + await snapshotGasCost(liquidityMath.getGasCostOfAddDelta(15, -4)) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/NoDelegateCall.spec.ts b/lib/uniswap/v3-core/test/NoDelegateCall.spec.ts new file mode 100644 index 0000000..c46af93 --- /dev/null +++ b/lib/uniswap/v3-core/test/NoDelegateCall.spec.ts @@ -0,0 +1,54 @@ +import { Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { NoDelegateCallTest } from '../typechain/NoDelegateCallTest' +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' + +describe('NoDelegateCall', () => { + let wallet: Wallet, other: Wallet + + let loadFixture: ReturnType + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + loadFixture = waffle.createFixtureLoader([wallet, other]) + }) + + const noDelegateCallFixture = async () => { + const noDelegateCallTestFactory = await ethers.getContractFactory('NoDelegateCallTest') + const noDelegateCallTest = (await noDelegateCallTestFactory.deploy()) as NoDelegateCallTest + const minimalProxyFactory = new ethers.ContractFactory( + noDelegateCallTestFactory.interface, + `3d602d80600a3d3981f3363d3d373d3d3d363d73${noDelegateCallTest.address.slice(2)}5af43d82803e903d91602b57fd5bf3`, + wallet + ) + const proxy = (await minimalProxyFactory.deploy()) as NoDelegateCallTest + return { noDelegateCallTest, proxy } + } + + let base: NoDelegateCallTest + let proxy: NoDelegateCallTest + + beforeEach('deploy test contracts', async () => { + ;({ noDelegateCallTest: base, proxy } = await loadFixture(noDelegateCallFixture)) + }) + + it('runtime overhead', async () => { + await snapshotGasCost( + (await base.getGasCostOfCannotBeDelegateCalled()).sub(await base.getGasCostOfCanBeDelegateCalled()) + ) + }) + + it('proxy can call the method without the modifier', async () => { + await proxy.canBeDelegateCalled() + }) + it('proxy cannot call the method with the modifier', async () => { + await expect(proxy.cannotBeDelegateCalled()).to.be.reverted + }) + + it('can call the method that calls into a private method with the modifier', async () => { + await base.callsIntoNoDelegateCallFunction() + }) + it('proxy cannot call the method that calls a private method with the modifier', async () => { + await expect(proxy.callsIntoNoDelegateCallFunction()).to.be.reverted + }) +}) diff --git a/lib/uniswap/v3-core/test/Oracle.spec.ts b/lib/uniswap/v3-core/test/Oracle.spec.ts new file mode 100644 index 0000000..36e6fd0 --- /dev/null +++ b/lib/uniswap/v3-core/test/Oracle.spec.ts @@ -0,0 +1,792 @@ +import { BigNumber, BigNumberish, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { OracleTest } from '../typechain/OracleTest' +import checkObservationEquals from './shared/checkObservationEquals' +import { expect } from './shared/expect' +import { TEST_POOL_START_TIME } from './shared/fixtures' +import snapshotGasCost from './shared/snapshotGasCost' +import { MaxUint128 } from './shared/utilities' + +describe('Oracle', () => { + let wallet: Wallet, other: Wallet + + let loadFixture: ReturnType + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + loadFixture = waffle.createFixtureLoader([wallet, other]) + }) + + const oracleFixture = async () => { + const oracleTestFactory = await ethers.getContractFactory('OracleTest') + return (await oracleTestFactory.deploy()) as OracleTest + } + + const initializedOracleFixture = async () => { + const oracle = await oracleFixture() + await oracle.initialize({ + time: 0, + tick: 0, + liquidity: 0, + }) + return oracle + } + + describe('#initialize', () => { + let oracle: OracleTest + beforeEach('deploy test oracle', async () => { + oracle = await loadFixture(oracleFixture) + }) + it('index is 0', async () => { + await oracle.initialize({ liquidity: 1, tick: 1, time: 1 }) + expect(await oracle.index()).to.eq(0) + }) + it('cardinality is 1', async () => { + await oracle.initialize({ liquidity: 1, tick: 1, time: 1 }) + expect(await oracle.cardinality()).to.eq(1) + }) + it('cardinality next is 1', async () => { + await oracle.initialize({ liquidity: 1, tick: 1, time: 1 }) + expect(await oracle.cardinalityNext()).to.eq(1) + }) + it('sets first slot timestamp only', async () => { + await oracle.initialize({ liquidity: 1, tick: 1, time: 1 }) + checkObservationEquals(await oracle.observations(0), { + initialized: true, + blockTimestamp: 1, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: 0, + }) + }) + it('gas', async () => { + await snapshotGasCost(oracle.initialize({ liquidity: 1, tick: 1, time: 1 })) + }) + }) + + describe('#grow', () => { + let oracle: OracleTest + beforeEach('deploy initialized test oracle', async () => { + oracle = await loadFixture(initializedOracleFixture) + }) + + it('increases the cardinality next for the first call', async () => { + await oracle.grow(5) + expect(await oracle.index()).to.eq(0) + expect(await oracle.cardinality()).to.eq(1) + expect(await oracle.cardinalityNext()).to.eq(5) + }) + + it('does not touch the first slot', async () => { + await oracle.grow(5) + checkObservationEquals(await oracle.observations(0), { + secondsPerLiquidityCumulativeX128: 0, + tickCumulative: 0, + blockTimestamp: 0, + initialized: true, + }) + }) + + it('is no op if oracle is already gte that size', async () => { + await oracle.grow(5) + await oracle.grow(3) + expect(await oracle.index()).to.eq(0) + expect(await oracle.cardinality()).to.eq(1) + expect(await oracle.cardinalityNext()).to.eq(5) + }) + + it('adds data to all the slots', async () => { + await oracle.grow(5) + for (let i = 1; i < 5; i++) { + checkObservationEquals(await oracle.observations(i), { + secondsPerLiquidityCumulativeX128: 0, + tickCumulative: 0, + blockTimestamp: 1, + initialized: false, + }) + } + }) + + it('grow after wrap', async () => { + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 2, liquidity: 1, tick: 1 }) // index is now 1 + await oracle.update({ advanceTimeBy: 2, liquidity: 1, tick: 1 }) // index is now 0 again + expect(await oracle.index()).to.eq(0) + await oracle.grow(3) + expect(await oracle.index()).to.eq(0) + expect(await oracle.cardinality()).to.eq(2) + expect(await oracle.cardinalityNext()).to.eq(3) + }) + + it('gas for growing by 1 slot when index == cardinality - 1', async () => { + await snapshotGasCost(oracle.grow(2)) + }) + + it('gas for growing by 10 slots when index == cardinality - 1', async () => { + await snapshotGasCost(oracle.grow(11)) + }) + + it('gas for growing by 1 slot when index != cardinality - 1', async () => { + await oracle.grow(2) + await snapshotGasCost(oracle.grow(3)) + }) + + it('gas for growing by 10 slots when index != cardinality - 1', async () => { + await oracle.grow(2) + await snapshotGasCost(oracle.grow(12)) + }) + }) + + describe('#write', () => { + let oracle: OracleTest + + beforeEach('deploy initialized test oracle', async () => { + oracle = await loadFixture(initializedOracleFixture) + }) + + it('single element array gets overwritten', async () => { + await oracle.update({ advanceTimeBy: 1, tick: 2, liquidity: 5 }) + expect(await oracle.index()).to.eq(0) + checkObservationEquals(await oracle.observations(0), { + initialized: true, + secondsPerLiquidityCumulativeX128: '340282366920938463463374607431768211456', + tickCumulative: 0, + blockTimestamp: 1, + }) + await oracle.update({ advanceTimeBy: 5, tick: -1, liquidity: 8 }) + expect(await oracle.index()).to.eq(0) + checkObservationEquals(await oracle.observations(0), { + initialized: true, + secondsPerLiquidityCumulativeX128: '680564733841876926926749214863536422912', + tickCumulative: 10, + blockTimestamp: 6, + }) + await oracle.update({ advanceTimeBy: 3, tick: 2, liquidity: 3 }) + expect(await oracle.index()).to.eq(0) + checkObservationEquals(await oracle.observations(0), { + initialized: true, + secondsPerLiquidityCumulativeX128: '808170621437228850725514692650449502208', + tickCumulative: 7, + blockTimestamp: 9, + }) + }) + + it('does nothing if time has not changed', async () => { + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 1, tick: 3, liquidity: 2 }) + expect(await oracle.index()).to.eq(1) + await oracle.update({ advanceTimeBy: 0, tick: -5, liquidity: 9 }) + expect(await oracle.index()).to.eq(1) + }) + + it('writes an index if time has changed', async () => { + await oracle.grow(3) + await oracle.update({ advanceTimeBy: 6, tick: 3, liquidity: 2 }) + expect(await oracle.index()).to.eq(1) + await oracle.update({ advanceTimeBy: 4, tick: -5, liquidity: 9 }) + + expect(await oracle.index()).to.eq(2) + checkObservationEquals(await oracle.observations(1), { + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: '2041694201525630780780247644590609268736', + initialized: true, + blockTimestamp: 6, + }) + }) + + it('grows cardinality when writing past', async () => { + await oracle.grow(2) + await oracle.grow(4) + expect(await oracle.cardinality()).to.eq(1) + await oracle.update({ advanceTimeBy: 3, tick: 5, liquidity: 6 }) + expect(await oracle.cardinality()).to.eq(4) + await oracle.update({ advanceTimeBy: 4, tick: 6, liquidity: 4 }) + expect(await oracle.cardinality()).to.eq(4) + expect(await oracle.index()).to.eq(2) + checkObservationEquals(await oracle.observations(2), { + secondsPerLiquidityCumulativeX128: '1247702012043441032699040227249816775338', + tickCumulative: 20, + initialized: true, + blockTimestamp: 7, + }) + }) + + it('wraps around', async () => { + await oracle.grow(3) + await oracle.update({ advanceTimeBy: 3, tick: 1, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 4, tick: 2, liquidity: 3 }) + await oracle.update({ advanceTimeBy: 5, tick: 3, liquidity: 4 }) + + expect(await oracle.index()).to.eq(0) + + checkObservationEquals(await oracle.observations(0), { + secondsPerLiquidityCumulativeX128: '2268549112806256423089164049545121409706', + tickCumulative: 14, + initialized: true, + blockTimestamp: 12, + }) + }) + + it('accumulates liquidity', async () => { + await oracle.grow(4) + + await oracle.update({ advanceTimeBy: 3, tick: 3, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 4, tick: -7, liquidity: 6 }) + await oracle.update({ advanceTimeBy: 5, tick: -2, liquidity: 4 }) + + expect(await oracle.index()).to.eq(3) + + checkObservationEquals(await oracle.observations(1), { + initialized: true, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: '1020847100762815390390123822295304634368', + blockTimestamp: 3, + }) + checkObservationEquals(await oracle.observations(2), { + initialized: true, + tickCumulative: 12, + secondsPerLiquidityCumulativeX128: '1701411834604692317316873037158841057280', + blockTimestamp: 7, + }) + checkObservationEquals(await oracle.observations(3), { + initialized: true, + tickCumulative: -23, + secondsPerLiquidityCumulativeX128: '1984980473705474370203018543351981233493', + blockTimestamp: 12, + }) + checkObservationEquals(await oracle.observations(4), { + initialized: false, + tickCumulative: 0, + secondsPerLiquidityCumulativeX128: 0, + blockTimestamp: 0, + }) + }) + }) + + describe('#observe', () => { + describe('before initialization', async () => { + let oracle: OracleTest + beforeEach('deploy test oracle', async () => { + oracle = await loadFixture(oracleFixture) + }) + + const observeSingle = async (secondsAgo: number) => { + const { + tickCumulatives: [tickCumulative], + secondsPerLiquidityCumulativeX128s: [secondsPerLiquidityCumulativeX128], + } = await oracle.observe([secondsAgo]) + return { secondsPerLiquidityCumulativeX128, tickCumulative } + } + + it('fails before initialize', async () => { + await expect(observeSingle(0)).to.be.revertedWith('I') + }) + + it('fails if an older observation does not exist', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 5 }) + await expect(observeSingle(1)).to.be.revertedWith('OLD') + }) + + it('does not fail across overflow boundary', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 2 ** 32 - 1 }) + await oracle.advanceTime(2) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(1) + expect(tickCumulative).to.be.eq(2) + expect(secondsPerLiquidityCumulativeX128).to.be.eq('85070591730234615865843651857942052864') + }) + + it('interpolates correctly at max liquidity', async () => { + await oracle.initialize({ liquidity: MaxUint128, tick: 0, time: 0 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 13, tick: 0, liquidity: 0 }) + let { secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(13) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(6)) + expect(secondsPerLiquidityCumulativeX128).to.eq(7) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(12)) + expect(secondsPerLiquidityCumulativeX128).to.eq(1) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(13)) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + }) + + it('interpolates correctly at min liquidity', async () => { + await oracle.initialize({ liquidity: 0, tick: 0, time: 0 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 13, tick: 0, liquidity: MaxUint128 }) + let { secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(13).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(6)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(7).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(12)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(1).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(13)) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + }) + + it('interpolates the same as 0 liquidity for 1 liquidity', async () => { + await oracle.initialize({ liquidity: 1, tick: 0, time: 0 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 13, tick: 0, liquidity: MaxUint128 }) + let { secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(13).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(6)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(7).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(12)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(1).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(13)) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + }) + + it('interpolates correctly across uint32 seconds boundaries', async () => { + // setup + await oracle.initialize({ liquidity: 0, tick: 0, time: 0 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 2 ** 32 - 6, tick: 0, liquidity: 0 }) + let { secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(2 ** 32 - 6).shl(128)) + await oracle.update({ advanceTimeBy: 13, tick: 0, liquidity: 0 }) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(0)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(7).shl(128)) + + // interpolation checks + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(3)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(4).shl(128)) + ;({ secondsPerLiquidityCumulativeX128 } = await observeSingle(8)) + expect(secondsPerLiquidityCumulativeX128).to.eq(BigNumber.from(2 ** 32 - 1).shl(128)) + }) + + it('single observation at current time', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 5 }) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + }) + + it('single observation in past but not earlier than secondsAgo', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 5 }) + await oracle.advanceTime(3) + await expect(observeSingle(4)).to.be.revertedWith('OLD') + }) + + it('single observation in past at exactly seconds ago', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 5 }) + await oracle.advanceTime(3) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(3) + expect(tickCumulative).to.eq(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + }) + + it('single observation in past counterfactual in past', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 5 }) + await oracle.advanceTime(3) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(1) + expect(tickCumulative).to.eq(4) + expect(secondsPerLiquidityCumulativeX128).to.eq('170141183460469231731687303715884105728') + }) + + it('single observation in past counterfactual now', async () => { + await oracle.initialize({ liquidity: 4, tick: 2, time: 5 }) + await oracle.advanceTime(3) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(6) + expect(secondsPerLiquidityCumulativeX128).to.eq('255211775190703847597530955573826158592') + }) + + it('two observations in chronological order 0 seconds ago exact', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(-20) + expect(secondsPerLiquidityCumulativeX128).to.eq('272225893536750770770699685945414569164') + }) + + it('two observations in chronological order 0 seconds ago counterfactual', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.advanceTime(7) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(-13) + expect(secondsPerLiquidityCumulativeX128).to.eq('1463214177760035392892510811956603309260') + }) + + it('two observations in chronological order seconds ago is exactly on first observation', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.advanceTime(7) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(11) + expect(tickCumulative).to.eq(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + }) + + it('two observations in chronological order seconds ago is between first and second', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.advanceTime(7) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(9) + expect(tickCumulative).to.eq(-10) + expect(secondsPerLiquidityCumulativeX128).to.eq('136112946768375385385349842972707284582') + }) + + it('two observations in reverse order 0 seconds ago exact', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 3, tick: -5, liquidity: 4 }) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(-17) + expect(secondsPerLiquidityCumulativeX128).to.eq('782649443918158465965761597093066886348') + }) + + it('two observations in reverse order 0 seconds ago counterfactual', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 3, tick: -5, liquidity: 4 }) + await oracle.advanceTime(7) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(-52) + expect(secondsPerLiquidityCumulativeX128).to.eq('1378143586029800777026667160098661256396') + }) + + it('two observations in reverse order seconds ago is exactly on first observation', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 3, tick: -5, liquidity: 4 }) + await oracle.advanceTime(7) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(10) + expect(tickCumulative).to.eq(-20) + expect(secondsPerLiquidityCumulativeX128).to.eq('272225893536750770770699685945414569164') + }) + + it('two observations in reverse order seconds ago is between first and second', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.grow(2) + await oracle.update({ advanceTimeBy: 4, tick: 1, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 3, tick: -5, liquidity: 4 }) + await oracle.advanceTime(7) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(9) + expect(tickCumulative).to.eq(-19) + expect(secondsPerLiquidityCumulativeX128).to.eq('442367076997220002502386989661298674892') + }) + + it('can fetch multiple observations', async () => { + await oracle.initialize({ time: 5, tick: 2, liquidity: BigNumber.from(2).pow(15) }) + await oracle.grow(4) + await oracle.update({ advanceTimeBy: 13, tick: 6, liquidity: BigNumber.from(2).pow(12) }) + await oracle.advanceTime(5) + + const { tickCumulatives, secondsPerLiquidityCumulativeX128s } = await oracle.observe([0, 3, 8, 13, 15, 18]) + expect(tickCumulatives).to.have.lengthOf(6) + expect(tickCumulatives[0]).to.eq(56) + expect(tickCumulatives[1]).to.eq(38) + expect(tickCumulatives[2]).to.eq(20) + expect(tickCumulatives[3]).to.eq(10) + expect(tickCumulatives[4]).to.eq(6) + expect(tickCumulatives[5]).to.eq(0) + expect(secondsPerLiquidityCumulativeX128s).to.have.lengthOf(6) + expect(secondsPerLiquidityCumulativeX128s[0]).to.eq('550383467004691728624232610897330176') + expect(secondsPerLiquidityCumulativeX128s[1]).to.eq('301153217795020002454768787094765568') + expect(secondsPerLiquidityCumulativeX128s[2]).to.eq('103845937170696552570609926584401920') + expect(secondsPerLiquidityCumulativeX128s[3]).to.eq('51922968585348276285304963292200960') + expect(secondsPerLiquidityCumulativeX128s[4]).to.eq('31153781151208965771182977975320576') + expect(secondsPerLiquidityCumulativeX128s[5]).to.eq(0) + }) + + it('gas for observe since most recent', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.advanceTime(2) + await snapshotGasCost(oracle.getGasCostOfObserve([1])) + }) + + it('gas for single observation at current time', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await snapshotGasCost(oracle.getGasCostOfObserve([0])) + }) + + it('gas for single observation at current time counterfactually computed', async () => { + await oracle.initialize({ liquidity: 5, tick: -5, time: 5 }) + await oracle.advanceTime(5) + await snapshotGasCost(oracle.getGasCostOfObserve([0])) + }) + }) + + for (const startingTime of [5, 2 ** 32 - 5]) { + describe(`initialized with 5 observations with starting time of ${startingTime}`, () => { + const oracleFixture5Observations = async () => { + const oracle = await oracleFixture() + await oracle.initialize({ liquidity: 5, tick: -5, time: startingTime }) + await oracle.grow(5) + await oracle.update({ advanceTimeBy: 3, tick: 1, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 2, tick: -6, liquidity: 4 }) + await oracle.update({ advanceTimeBy: 4, tick: -2, liquidity: 4 }) + await oracle.update({ advanceTimeBy: 1, tick: -2, liquidity: 9 }) + await oracle.update({ advanceTimeBy: 3, tick: 4, liquidity: 2 }) + await oracle.update({ advanceTimeBy: 6, tick: 6, liquidity: 7 }) + return oracle + } + let oracle: OracleTest + beforeEach('set up observations', async () => { + oracle = await loadFixture(oracleFixture5Observations) + }) + + const observeSingle = async (secondsAgo: number) => { + const { + tickCumulatives: [tickCumulative], + secondsPerLiquidityCumulativeX128s: [secondsPerLiquidityCumulativeX128], + } = await oracle.observe([secondsAgo]) + return { secondsPerLiquidityCumulativeX128, tickCumulative } + } + + it('index, cardinality, cardinality next', async () => { + expect(await oracle.index()).to.eq(1) + expect(await oracle.cardinality()).to.eq(5) + expect(await oracle.cardinalityNext()).to.eq(5) + }) + it('latest observation same time as latest', async () => { + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(-21) + expect(secondsPerLiquidityCumulativeX128).to.eq('2104079302127802832415199655953100107502') + }) + it('latest observation 5 seconds after latest', async () => { + await oracle.advanceTime(5) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(5) + expect(tickCumulative).to.eq(-21) + expect(secondsPerLiquidityCumulativeX128).to.eq('2104079302127802832415199655953100107502') + }) + it('current observation 5 seconds after latest', async () => { + await oracle.advanceTime(5) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(0) + expect(tickCumulative).to.eq(9) + expect(secondsPerLiquidityCumulativeX128).to.eq('2347138135642758877746181518404363115684') + }) + it('between latest observation and just before latest observation at same time as latest', async () => { + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(3) + expect(tickCumulative).to.eq(-33) + expect(secondsPerLiquidityCumulativeX128).to.eq('1593655751746395137220137744805447790318') + }) + it('between latest observation and just before latest observation after the latest observation', async () => { + await oracle.advanceTime(5) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(8) + expect(tickCumulative).to.eq(-33) + expect(secondsPerLiquidityCumulativeX128).to.eq('1593655751746395137220137744805447790318') + }) + it('older than oldest reverts', async () => { + await expect(observeSingle(15)).to.be.revertedWith('OLD') + await oracle.advanceTime(5) + await expect(observeSingle(20)).to.be.revertedWith('OLD') + }) + it('oldest observation', async () => { + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(14) + expect(tickCumulative).to.eq(-13) + expect(secondsPerLiquidityCumulativeX128).to.eq('544451787073501541541399371890829138329') + }) + it('oldest observation after some time', async () => { + await oracle.advanceTime(6) + const { tickCumulative, secondsPerLiquidityCumulativeX128 } = await observeSingle(20) + expect(tickCumulative).to.eq(-13) + expect(secondsPerLiquidityCumulativeX128).to.eq('544451787073501541541399371890829138329') + }) + + it('fetch many values', async () => { + await oracle.advanceTime(6) + const { tickCumulatives, secondsPerLiquidityCumulativeX128s } = await oracle.observe([ + 20, 17, 13, 10, 5, 1, 0, + ]) + expect({ + tickCumulatives: tickCumulatives.map((tc) => tc.toNumber()), + secondsPerLiquidityCumulativeX128s: secondsPerLiquidityCumulativeX128s.map((lc) => lc.toString()), + }).to.matchSnapshot() + }) + + it('gas all of last 20 seconds', async () => { + await oracle.advanceTime(6) + await snapshotGasCost( + oracle.getGasCostOfObserve([20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) + ) + }) + + it('gas latest equal', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([0])) + }) + it('gas latest transform', async () => { + await oracle.advanceTime(5) + await snapshotGasCost(oracle.getGasCostOfObserve([0])) + }) + it('gas oldest', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([14])) + }) + it('gas between oldest and oldest + 1', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([13])) + }) + it('gas middle', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([5])) + }) + }) + } + }) + + describe.skip('full oracle', function () { + this.timeout(1_200_000) + + let oracle: OracleTest + + const BATCH_SIZE = 300 + + const STARTING_TIME = TEST_POOL_START_TIME + + const maxedOutOracleFixture = async () => { + const oracle = await oracleFixture() + await oracle.initialize({ liquidity: 0, tick: 0, time: STARTING_TIME }) + let cardinalityNext = await oracle.cardinalityNext() + while (cardinalityNext < 65535) { + const growTo = Math.min(65535, cardinalityNext + BATCH_SIZE) + console.log('growing from', cardinalityNext, 'to', growTo) + await oracle.grow(growTo) + cardinalityNext = growTo + } + + for (let i = 0; i < 65535; i += BATCH_SIZE) { + console.log('batch update starting at', i) + const batch = Array(BATCH_SIZE) + .fill(null) + .map((_, j) => ({ + advanceTimeBy: 13, + tick: -i - j, + liquidity: i + j, + })) + await oracle.batchUpdate(batch) + } + + return oracle + } + + beforeEach('create a full oracle', async () => { + oracle = await loadFixture(maxedOutOracleFixture) + }) + + it('has max cardinality next', async () => { + expect(await oracle.cardinalityNext()).to.eq(65535) + }) + + it('has max cardinality', async () => { + expect(await oracle.cardinality()).to.eq(65535) + }) + + it('index wrapped around', async () => { + expect(await oracle.index()).to.eq(165) + }) + + async function checkObserve( + secondsAgo: number, + expected?: { tickCumulative: BigNumberish; secondsPerLiquidityCumulativeX128: BigNumberish } + ) { + const { tickCumulatives, secondsPerLiquidityCumulativeX128s } = await oracle.observe([secondsAgo]) + const check = { + tickCumulative: tickCumulatives[0].toString(), + secondsPerLiquidityCumulativeX128: secondsPerLiquidityCumulativeX128s[0].toString(), + } + if (typeof expected === 'undefined') { + expect(check).to.matchSnapshot() + } else { + expect(check).to.deep.eq({ + tickCumulative: expected.tickCumulative.toString(), + secondsPerLiquidityCumulativeX128: expected.secondsPerLiquidityCumulativeX128.toString(), + }) + } + } + + it('can observe into the ordered portion with exact seconds ago', async () => { + await checkObserve(100 * 13, { + secondsPerLiquidityCumulativeX128: '60465049086512033878831623038233202591033', + tickCumulative: '-27970560813', + }) + }) + + it('can observe into the ordered portion with unexact seconds ago', async () => { + await checkObserve(100 * 13 + 5, { + secondsPerLiquidityCumulativeX128: '60465023149565257990964350912969670793706', + tickCumulative: '-27970232823', + }) + }) + + it('can observe at exactly the latest observation', async () => { + await checkObserve(0, { + secondsPerLiquidityCumulativeX128: '60471787506468701386237800669810720099776', + tickCumulative: '-28055903863', + }) + }) + + it('can observe at exactly the latest observation after some time passes', async () => { + await oracle.advanceTime(5) + await checkObserve(5, { + secondsPerLiquidityCumulativeX128: '60471787506468701386237800669810720099776', + tickCumulative: '-28055903863', + }) + }) + + it('can observe after the latest observation counterfactual', async () => { + await oracle.advanceTime(5) + await checkObserve(3, { + secondsPerLiquidityCumulativeX128: '60471797865298117996489508104462919730461', + tickCumulative: '-28056035261', + }) + }) + + it('can observe into the unordered portion of array at exact seconds ago of observation', async () => { + await checkObserve(200 * 13, { + secondsPerLiquidityCumulativeX128: '60458300386499273141628780395875293027404', + tickCumulative: '-27885347763', + }) + }) + + it('can observe into the unordered portion of array at seconds ago between observations', async () => { + await checkObserve(200 * 13 + 5, { + secondsPerLiquidityCumulativeX128: '60458274409952896081377821330361274907140', + tickCumulative: '-27885020273', + }) + }) + + it('can observe the oldest observation 13*65534 seconds ago', async () => { + await checkObserve(13 * 65534, { + secondsPerLiquidityCumulativeX128: '33974356747348039873972993881117400879779', + tickCumulative: '-175890', + }) + }) + + it('can observe the oldest observation 13*65534 + 5 seconds ago if time has elapsed', async () => { + await oracle.advanceTime(5) + await checkObserve(13 * 65534 + 5, { + secondsPerLiquidityCumulativeX128: '33974356747348039873972993881117400879779', + tickCumulative: '-175890', + }) + }) + + it('gas cost of observe(0)', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([0])) + }) + it('gas cost of observe(200 * 13)', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([200 + 13])) + }) + it('gas cost of observe(200 * 13 + 5)', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([200 + 13 + 5])) + }) + it('gas cost of observe(0) after 5 seconds', async () => { + await oracle.advanceTime(5) + await snapshotGasCost(oracle.getGasCostOfObserve([0])) + }) + it('gas cost of observe(5) after 5 seconds', async () => { + await oracle.advanceTime(5) + await snapshotGasCost(oracle.getGasCostOfObserve([5])) + }) + it('gas cost of observe(oldest)', async () => { + await snapshotGasCost(oracle.getGasCostOfObserve([65534 * 13])) + }) + it('gas cost of observe(oldest) after 5 seconds', async () => { + await oracle.advanceTime(5) + await snapshotGasCost(oracle.getGasCostOfObserve([65534 * 13 + 5])) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/SqrtPriceMath.spec.ts b/lib/uniswap/v3-core/test/SqrtPriceMath.spec.ts new file mode 100644 index 0000000..ef01245 --- /dev/null +++ b/lib/uniswap/v3-core/test/SqrtPriceMath.spec.ts @@ -0,0 +1,379 @@ +import { BigNumber, constants } from 'ethers' +import { ethers } from 'hardhat' +import { SqrtPriceMathTest } from '../typechain/SqrtPriceMathTest' + +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' +import { encodePriceSqrt, expandTo18Decimals, MaxUint128 } from './shared/utilities' + +const { + constants: { MaxUint256 }, +} = ethers + +describe('SqrtPriceMath', () => { + let sqrtPriceMath: SqrtPriceMathTest + before(async () => { + const sqrtPriceMathTestFactory = await ethers.getContractFactory('SqrtPriceMathTest') + sqrtPriceMath = (await sqrtPriceMathTestFactory.deploy()) as SqrtPriceMathTest + }) + + describe('#getNextSqrtPriceFromInput', () => { + it('fails if price is zero', async () => { + await expect(sqrtPriceMath.getNextSqrtPriceFromInput(0, 0, expandTo18Decimals(1).div(10), false)).to.be.reverted + }) + + it('fails if liquidity is zero', async () => { + await expect(sqrtPriceMath.getNextSqrtPriceFromInput(1, 0, expandTo18Decimals(1).div(10), true)).to.be.reverted + }) + + it('fails if input amount overflows the price', async () => { + const price = BigNumber.from(2).pow(160).sub(1) + const liquidity = 1024 + const amountIn = 1024 + await expect(sqrtPriceMath.getNextSqrtPriceFromInput(price, liquidity, amountIn, false)).to.be.reverted + }) + + it('any input amount cannot underflow the price', async () => { + const price = 1 + const liquidity = 1 + const amountIn = BigNumber.from(2).pow(255) + expect(await sqrtPriceMath.getNextSqrtPriceFromInput(price, liquidity, amountIn, true)).to.eq(1) + }) + + it('returns input price if amount in is zero and zeroForOne = true', async () => { + const price = encodePriceSqrt(1, 1) + expect(await sqrtPriceMath.getNextSqrtPriceFromInput(price, expandTo18Decimals(1).div(10), 0, true)).to.eq(price) + }) + + it('returns input price if amount in is zero and zeroForOne = false', async () => { + const price = encodePriceSqrt(1, 1) + expect(await sqrtPriceMath.getNextSqrtPriceFromInput(price, expandTo18Decimals(1).div(10), 0, false)).to.eq(price) + }) + + it('returns the minimum price for max inputs', async () => { + const sqrtP = BigNumber.from(2).pow(160).sub(1) + const liquidity = MaxUint128 + const maxAmountNoOverflow = MaxUint256.sub(liquidity.shl(96).div(sqrtP)) + expect(await sqrtPriceMath.getNextSqrtPriceFromInput(sqrtP, liquidity, maxAmountNoOverflow, true)).to.eq('1') + }) + + it('input amount of 0.1 token1', async () => { + const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromInput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + false + ) + expect(sqrtQ).to.eq('87150978765690771352898345369') + }) + + it('input amount of 0.1 token0', async () => { + const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromInput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + true + ) + expect(sqrtQ).to.eq('72025602285694852357767227579') + }) + + it('amountIn > type(uint96).max and zeroForOne = true', async () => { + expect( + await sqrtPriceMath.getNextSqrtPriceFromInput( + encodePriceSqrt(1, 1), + expandTo18Decimals(10), + BigNumber.from(2).pow(100), + true + ) + // perfect answer: + // https://www.wolframalpha.com/input/?i=624999999995069620+-+%28%281e19+*+1+%2F+%281e19+%2B+2%5E100+*+1%29%29+*+2%5E96%29 + ).to.eq('624999999995069620') + }) + + it('can return 1 with enough amountIn and zeroForOne = true', async () => { + expect( + await sqrtPriceMath.getNextSqrtPriceFromInput(encodePriceSqrt(1, 1), 1, constants.MaxUint256.div(2), true) + ).to.eq(1) + }) + + it('zeroForOne = true gas', async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromInput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + true + ) + ) + }) + + it('zeroForOne = false gas', async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromInput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + false + ) + ) + }) + }) + + describe('#getNextSqrtPriceFromOutput', () => { + it('fails if price is zero', async () => { + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(0, 0, expandTo18Decimals(1).div(10), false)).to.be.reverted + }) + + it('fails if liquidity is zero', async () => { + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(1, 0, expandTo18Decimals(1).div(10), true)).to.be.reverted + }) + + it('fails if output amount is exactly the virtual reserves of token0', async () => { + const price = '20282409603651670423947251286016' + const liquidity = 1024 + const amountOut = 4 + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, false)).to.be.reverted + }) + + it('fails if output amount is greater than virtual reserves of token0', async () => { + const price = '20282409603651670423947251286016' + const liquidity = 1024 + const amountOut = 5 + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, false)).to.be.reverted + }) + + it('fails if output amount is greater than virtual reserves of token1', async () => { + const price = '20282409603651670423947251286016' + const liquidity = 1024 + const amountOut = 262145 + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, true)).to.be.reverted + }) + + it('fails if output amount is exactly the virtual reserves of token1', async () => { + const price = '20282409603651670423947251286016' + const liquidity = 1024 + const amountOut = 262144 + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, true)).to.be.reverted + }) + + it('succeeds if output amount is just less than the virtual reserves of token1', async () => { + const price = '20282409603651670423947251286016' + const liquidity = 1024 + const amountOut = 262143 + const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, true) + expect(sqrtQ).to.eq('77371252455336267181195264') + }) + + it('puzzling echidna test', async () => { + const price = '20282409603651670423947251286016' + const liquidity = 1024 + const amountOut = 4 + + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, false)).to.be.reverted + }) + + it('returns input price if amount in is zero and zeroForOne = true', async () => { + const price = encodePriceSqrt(1, 1) + expect(await sqrtPriceMath.getNextSqrtPriceFromOutput(price, expandTo18Decimals(1).div(10), 0, true)).to.eq(price) + }) + + it('returns input price if amount in is zero and zeroForOne = false', async () => { + const price = encodePriceSqrt(1, 1) + expect(await sqrtPriceMath.getNextSqrtPriceFromOutput(price, expandTo18Decimals(1).div(10), 0, false)).to.eq( + price + ) + }) + + it('output amount of 0.1 token1', async () => { + const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromOutput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + false + ) + expect(sqrtQ).to.eq('88031291682515930659493278152') + }) + + it('output amount of 0.1 token1', async () => { + const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromOutput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + true + ) + expect(sqrtQ).to.eq('71305346262837903834189555302') + }) + + it('reverts if amountOut is impossible in zero for one direction', async () => { + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(encodePriceSqrt(1, 1), 1, constants.MaxUint256, true)).to.be + .reverted + }) + + it('reverts if amountOut is impossible in one for zero direction', async () => { + await expect(sqrtPriceMath.getNextSqrtPriceFromOutput(encodePriceSqrt(1, 1), 1, constants.MaxUint256, false)).to + .be.reverted + }) + + it('zeroForOne = true gas', async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromOutput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + true + ) + ) + }) + + it('zeroForOne = false gas', async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetNextSqrtPriceFromOutput( + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + expandTo18Decimals(1).div(10), + false + ) + ) + }) + }) + + describe('#getAmount0Delta', () => { + it('returns 0 if liquidity is 0', async () => { + const amount0 = await sqrtPriceMath.getAmount0Delta(encodePriceSqrt(1, 1), encodePriceSqrt(2, 1), 0, true) + + expect(amount0).to.eq(0) + }) + it('returns 0 if prices are equal', async () => { + const amount0 = await sqrtPriceMath.getAmount0Delta(encodePriceSqrt(1, 1), encodePriceSqrt(1, 1), 0, true) + + expect(amount0).to.eq(0) + }) + + it('returns 0.1 amount1 for price of 1 to 1.21', async () => { + const amount0 = await sqrtPriceMath.getAmount0Delta( + encodePriceSqrt(1, 1), + encodePriceSqrt(121, 100), + expandTo18Decimals(1), + true + ) + expect(amount0).to.eq('90909090909090910') + + const amount0RoundedDown = await sqrtPriceMath.getAmount0Delta( + encodePriceSqrt(1, 1), + encodePriceSqrt(121, 100), + expandTo18Decimals(1), + false + ) + + expect(amount0RoundedDown).to.eq(amount0.sub(1)) + }) + + it('works for prices that overflow', async () => { + const amount0Up = await sqrtPriceMath.getAmount0Delta( + encodePriceSqrt(BigNumber.from(2).pow(90), 1), + encodePriceSqrt(BigNumber.from(2).pow(96), 1), + expandTo18Decimals(1), + true + ) + const amount0Down = await sqrtPriceMath.getAmount0Delta( + encodePriceSqrt(BigNumber.from(2).pow(90), 1), + encodePriceSqrt(BigNumber.from(2).pow(96), 1), + expandTo18Decimals(1), + false + ) + expect(amount0Up).to.eq(amount0Down.add(1)) + }) + + it(`gas cost for amount0 where roundUp = true`, async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetAmount0Delta( + encodePriceSqrt(100, 121), + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + true + ) + ) + }) + + it(`gas cost for amount0 where roundUp = true`, async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetAmount0Delta( + encodePriceSqrt(100, 121), + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + false + ) + ) + }) + }) + + describe('#getAmount1Delta', () => { + it('returns 0 if liquidity is 0', async () => { + const amount1 = await sqrtPriceMath.getAmount1Delta(encodePriceSqrt(1, 1), encodePriceSqrt(2, 1), 0, true) + + expect(amount1).to.eq(0) + }) + it('returns 0 if prices are equal', async () => { + const amount1 = await sqrtPriceMath.getAmount0Delta(encodePriceSqrt(1, 1), encodePriceSqrt(1, 1), 0, true) + + expect(amount1).to.eq(0) + }) + + it('returns 0.1 amount1 for price of 1 to 1.21', async () => { + const amount1 = await sqrtPriceMath.getAmount1Delta( + encodePriceSqrt(1, 1), + encodePriceSqrt(121, 100), + expandTo18Decimals(1), + true + ) + + expect(amount1).to.eq('100000000000000000') + const amount1RoundedDown = await sqrtPriceMath.getAmount1Delta( + encodePriceSqrt(1, 1), + encodePriceSqrt(121, 100), + expandTo18Decimals(1), + false + ) + + expect(amount1RoundedDown).to.eq(amount1.sub(1)) + }) + + it(`gas cost for amount0 where roundUp = true`, async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetAmount0Delta( + encodePriceSqrt(100, 121), + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + true + ) + ) + }) + + it(`gas cost for amount0 where roundUp = false`, async () => { + await snapshotGasCost( + sqrtPriceMath.getGasCostOfGetAmount0Delta( + encodePriceSqrt(100, 121), + encodePriceSqrt(1, 1), + expandTo18Decimals(1), + false + ) + ) + }) + }) + + describe('swap computation', () => { + it('sqrtP * sqrtQ overflows', async () => { + // getNextSqrtPriceInvariants(1025574284609383690408304870162715216695788925244,50015962439936049619261659728067971248,406,true) + const sqrtP = '1025574284609383690408304870162715216695788925244' + const liquidity = '50015962439936049619261659728067971248' + const zeroForOne = true + const amountIn = '406' + + const sqrtQ = await sqrtPriceMath.getNextSqrtPriceFromInput(sqrtP, liquidity, amountIn, zeroForOne) + expect(sqrtQ).to.eq('1025574284609383582644711336373707553698163132913') + + const amount0Delta = await sqrtPriceMath.getAmount0Delta(sqrtQ, sqrtP, liquidity, true) + expect(amount0Delta).to.eq('406') + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/SwapMath.spec.ts b/lib/uniswap/v3-core/test/SwapMath.spec.ts new file mode 100644 index 0000000..2dbd920 --- /dev/null +++ b/lib/uniswap/v3-core/test/SwapMath.spec.ts @@ -0,0 +1,324 @@ +import { BigNumber } from 'ethers' +import { ethers } from 'hardhat' +import { SwapMathTest } from '../typechain/SwapMathTest' + +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' +import { encodePriceSqrt, expandTo18Decimals } from './shared/utilities' +import { SqrtPriceMathTest } from '../typechain/SqrtPriceMathTest' + +describe('SwapMath', () => { + let swapMath: SwapMathTest + let sqrtPriceMath: SqrtPriceMathTest + before(async () => { + const swapMathTestFactory = await ethers.getContractFactory('SwapMathTest') + const sqrtPriceMathTestFactory = await ethers.getContractFactory('SqrtPriceMathTest') + swapMath = (await swapMathTestFactory.deploy()) as SwapMathTest + sqrtPriceMath = (await sqrtPriceMathTestFactory.deploy()) as SqrtPriceMathTest + }) + + describe('#computeSwapStep', () => { + it('exact amount in that gets capped at price target in one for zero', async () => { + const price = encodePriceSqrt(1, 1) + const priceTarget = encodePriceSqrt(101, 100) + const liquidity = expandTo18Decimals(2) + const amount = expandTo18Decimals(1) + const fee = 600 + const zeroForOne = false + + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + price, + priceTarget, + liquidity, + amount, + fee + ) + + expect(amountIn).to.eq('9975124224178055') + expect(feeAmount).to.eq('5988667735148') + expect(amountOut).to.eq('9925619580021728') + expect(amountIn.add(feeAmount), 'entire amount is not used').to.lt(amount) + + const priceAfterWholeInputAmount = await sqrtPriceMath.getNextSqrtPriceFromInput( + price, + liquidity, + amount, + zeroForOne + ) + + expect(sqrtQ, 'price is capped at price target').to.eq(priceTarget) + expect(sqrtQ, 'price is less than price after whole input amount').to.lt(priceAfterWholeInputAmount) + }) + + it('exact amount out that gets capped at price target in one for zero', async () => { + const price = encodePriceSqrt(1, 1) + const priceTarget = encodePriceSqrt(101, 100) + const liquidity = expandTo18Decimals(2) + const amount = expandTo18Decimals(1).mul(-1) + const fee = 600 + const zeroForOne = false + + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + price, + priceTarget, + liquidity, + amount, + fee + ) + + expect(amountIn).to.eq('9975124224178055') + expect(feeAmount).to.eq('5988667735148') + expect(amountOut).to.eq('9925619580021728') + expect(amountOut, 'entire amount out is not returned').to.lt(amount.mul(-1)) + + const priceAfterWholeOutputAmount = await sqrtPriceMath.getNextSqrtPriceFromOutput( + price, + liquidity, + amount.mul(-1), + zeroForOne + ) + + expect(sqrtQ, 'price is capped at price target').to.eq(priceTarget) + expect(sqrtQ, 'price is less than price after whole output amount').to.lt(priceAfterWholeOutputAmount) + }) + + it('exact amount in that is fully spent in one for zero', async () => { + const price = encodePriceSqrt(1, 1) + const priceTarget = encodePriceSqrt(1000, 100) + const liquidity = expandTo18Decimals(2) + const amount = expandTo18Decimals(1) + const fee = 600 + const zeroForOne = false + + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + price, + priceTarget, + liquidity, + amount, + fee + ) + + expect(amountIn).to.eq('999400000000000000') + expect(feeAmount).to.eq('600000000000000') + expect(amountOut).to.eq('666399946655997866') + expect(amountIn.add(feeAmount), 'entire amount is used').to.eq(amount) + + const priceAfterWholeInputAmountLessFee = await sqrtPriceMath.getNextSqrtPriceFromInput( + price, + liquidity, + amount.sub(feeAmount), + zeroForOne + ) + + expect(sqrtQ, 'price does not reach price target').to.be.lt(priceTarget) + expect(sqrtQ, 'price is equal to price after whole input amount').to.eq(priceAfterWholeInputAmountLessFee) + }) + + it('exact amount out that is fully received in one for zero', async () => { + const price = encodePriceSqrt(1, 1) + const priceTarget = encodePriceSqrt(10000, 100) + const liquidity = expandTo18Decimals(2) + const amount = expandTo18Decimals(1).mul(-1) + const fee = 600 + const zeroForOne = false + + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + price, + priceTarget, + liquidity, + amount, + fee + ) + + expect(amountIn).to.eq('2000000000000000000') + expect(feeAmount).to.eq('1200720432259356') + expect(amountOut).to.eq(amount.mul(-1)) + + const priceAfterWholeOutputAmount = await sqrtPriceMath.getNextSqrtPriceFromOutput( + price, + liquidity, + amount.mul(-1), + zeroForOne + ) + + expect(sqrtQ, 'price does not reach price target').to.be.lt(priceTarget) + expect(sqrtQ, 'price is less than price after whole output amount').to.eq(priceAfterWholeOutputAmount) + }) + + it('amount out is capped at the desired amount out', async () => { + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + BigNumber.from('417332158212080721273783715441582'), + BigNumber.from('1452870262520218020823638996'), + '159344665391607089467575320103', + '-1', + 1 + ) + expect(amountIn).to.eq('1') + expect(feeAmount).to.eq('1') + expect(amountOut).to.eq('1') // would be 2 if not capped + expect(sqrtQ).to.eq('417332158212080721273783715441581') + }) + + it('target price of 1 uses partial input amount', async () => { + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + BigNumber.from('2'), + BigNumber.from('1'), + '1', + '3915081100057732413702495386755767', + 1 + ) + expect(amountIn).to.eq('39614081257132168796771975168') + expect(feeAmount).to.eq('39614120871253040049813') + expect(amountIn.add(feeAmount)).to.be.lte('3915081100057732413702495386755767') + expect(amountOut).to.eq('0') + expect(sqrtQ).to.eq('1') + }) + + it('entire input amount taken as fee', async () => { + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + '2413', + '79887613182836312', + '1985041575832132834610021537970', + '10', + 1872 + ) + expect(amountIn).to.eq('0') + expect(feeAmount).to.eq('10') + expect(amountOut).to.eq('0') + expect(sqrtQ).to.eq('2413') + }) + + it('handles intermediate insufficient liquidity in zero for one exact output case', async () => { + const sqrtP = BigNumber.from('20282409603651670423947251286016') + const sqrtPTarget = sqrtP.mul(11).div(10) + const liquidity = 1024 + // virtual reserves of one are only 4 + // https://www.wolframalpha.com/input/?i=1024+%2F+%2820282409603651670423947251286016+%2F+2**96%29 + const amountRemaining = -4 + const feePips = 3000 + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + sqrtP, + sqrtPTarget, + liquidity, + amountRemaining, + feePips + ) + expect(amountOut).to.eq(0) + expect(sqrtQ).to.eq(sqrtPTarget) + expect(amountIn).to.eq(26215) + expect(feeAmount).to.eq(79) + }) + + it('handles intermediate insufficient liquidity in one for zero exact output case', async () => { + const sqrtP = BigNumber.from('20282409603651670423947251286016') + const sqrtPTarget = sqrtP.mul(9).div(10) + const liquidity = 1024 + // virtual reserves of zero are only 262144 + // https://www.wolframalpha.com/input/?i=1024+*+%2820282409603651670423947251286016+%2F+2**96%29 + const amountRemaining = -263000 + const feePips = 3000 + const { amountIn, amountOut, sqrtQ, feeAmount } = await swapMath.computeSwapStep( + sqrtP, + sqrtPTarget, + liquidity, + amountRemaining, + feePips + ) + expect(amountOut).to.eq(26214) + expect(sqrtQ).to.eq(sqrtPTarget) + expect(amountIn).to.eq(1) + expect(feeAmount).to.eq(1) + }) + + describe('gas', () => { + it('swap one for zero exact in capped', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(101, 100), + expandTo18Decimals(2), + expandTo18Decimals(1), + 600 + ) + ) + }) + it('swap zero for one exact in capped', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(99, 100), + expandTo18Decimals(2), + expandTo18Decimals(1), + 600 + ) + ) + }) + it('swap one for zero exact out capped', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(101, 100), + expandTo18Decimals(2), + expandTo18Decimals(1).mul(-1), + 600 + ) + ) + }) + it('swap zero for one exact out capped', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(99, 100), + expandTo18Decimals(2), + expandTo18Decimals(1).mul(-1), + 600 + ) + ) + }) + it('swap one for zero exact in partial', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(1010, 100), + expandTo18Decimals(2), + 1000, + 600 + ) + ) + }) + it('swap zero for one exact in partial', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(99, 1000), + expandTo18Decimals(2), + 1000, + 600 + ) + ) + }) + it('swap one for zero exact out partial', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(1010, 100), + expandTo18Decimals(2), + 1000, + 600 + ) + ) + }) + it('swap zero for one exact out partial', async () => { + await snapshotGasCost( + swapMath.getGasCostOfComputeSwapStep( + encodePriceSqrt(1, 1), + encodePriceSqrt(99, 1000), + expandTo18Decimals(2), + 1000, + 600 + ) + ) + }) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/Tick.spec.ts b/lib/uniswap/v3-core/test/Tick.spec.ts new file mode 100644 index 0000000..f4b04dc --- /dev/null +++ b/lib/uniswap/v3-core/test/Tick.spec.ts @@ -0,0 +1,329 @@ +import { ethers } from 'hardhat' +import { BigNumber } from 'ethers' +import { TickTest } from '../typechain/TickTest' +import { expect } from './shared/expect' +import { FeeAmount, getMaxLiquidityPerTick, TICK_SPACINGS } from './shared/utilities' + +const MaxUint128 = BigNumber.from(2).pow(128).sub(1) + +const { constants } = ethers + +describe('Tick', () => { + let tickTest: TickTest + + beforeEach('deploy TickTest', async () => { + const tickTestFactory = await ethers.getContractFactory('TickTest') + tickTest = (await tickTestFactory.deploy()) as TickTest + }) + + describe('#tickSpacingToMaxLiquidityPerTick', () => { + it('returns the correct value for low fee', async () => { + const maxLiquidityPerTick = await tickTest.tickSpacingToMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.LOW]) + expect(maxLiquidityPerTick).to.eq('1917569901783203986719870431555990') // 110.8 bits + expect(maxLiquidityPerTick).to.eq(getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.LOW])) + }) + it('returns the correct value for medium fee', async () => { + const maxLiquidityPerTick = await tickTest.tickSpacingToMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.MEDIUM]) + expect(maxLiquidityPerTick).to.eq('11505743598341114571880798222544994') // 113.1 bits + expect(maxLiquidityPerTick).to.eq(getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + }) + it('returns the correct value for high fee', async () => { + const maxLiquidityPerTick = await tickTest.tickSpacingToMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.HIGH]) + expect(maxLiquidityPerTick).to.eq('38350317471085141830651933667504588') // 114.7 bits + expect(maxLiquidityPerTick).to.eq(getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.HIGH])) + }) + it('returns the correct value for entire range', async () => { + const maxLiquidityPerTick = await tickTest.tickSpacingToMaxLiquidityPerTick(887272) + expect(maxLiquidityPerTick).to.eq(MaxUint128.div(3)) // 126 bits + expect(maxLiquidityPerTick).to.eq(getMaxLiquidityPerTick(887272)) + }) + it('returns the correct value for 2302', async () => { + const maxLiquidityPerTick = await tickTest.tickSpacingToMaxLiquidityPerTick(2302) + expect(maxLiquidityPerTick).to.eq('441351967472034323558203122479595605') // 118 bits + expect(maxLiquidityPerTick).to.eq(getMaxLiquidityPerTick(2302)) + }) + }) + + describe('#getFeeGrowthInside', () => { + it('returns all for two uninitialized ticks if tick is inside', async () => { + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, 0, 15, 15) + expect(feeGrowthInside0X128).to.eq(15) + expect(feeGrowthInside1X128).to.eq(15) + }) + it('returns 0 for two uninitialized ticks if tick is above', async () => { + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, 4, 15, 15) + expect(feeGrowthInside0X128).to.eq(0) + expect(feeGrowthInside1X128).to.eq(0) + }) + it('returns 0 for two uninitialized ticks if tick is below', async () => { + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, -4, 15, 15) + expect(feeGrowthInside0X128).to.eq(0) + expect(feeGrowthInside1X128).to.eq(0) + }) + + it('subtracts upper tick if below', async () => { + await tickTest.setTick(2, { + feeGrowthOutside0X128: 2, + feeGrowthOutside1X128: 3, + liquidityGross: 0, + liquidityNet: 0, + secondsPerLiquidityOutsideX128: 0, + tickCumulativeOutside: 0, + secondsOutside: 0, + initialized: true, + }) + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, 0, 15, 15) + expect(feeGrowthInside0X128).to.eq(13) + expect(feeGrowthInside1X128).to.eq(12) + }) + + it('subtracts lower tick if above', async () => { + await tickTest.setTick(-2, { + feeGrowthOutside0X128: 2, + feeGrowthOutside1X128: 3, + liquidityGross: 0, + liquidityNet: 0, + secondsPerLiquidityOutsideX128: 0, + tickCumulativeOutside: 0, + secondsOutside: 0, + initialized: true, + }) + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, 0, 15, 15) + expect(feeGrowthInside0X128).to.eq(13) + expect(feeGrowthInside1X128).to.eq(12) + }) + + it('subtracts upper and lower tick if inside', async () => { + await tickTest.setTick(-2, { + feeGrowthOutside0X128: 2, + feeGrowthOutside1X128: 3, + liquidityGross: 0, + liquidityNet: 0, + secondsPerLiquidityOutsideX128: 0, + tickCumulativeOutside: 0, + secondsOutside: 0, + initialized: true, + }) + await tickTest.setTick(2, { + feeGrowthOutside0X128: 4, + feeGrowthOutside1X128: 1, + liquidityGross: 0, + liquidityNet: 0, + secondsPerLiquidityOutsideX128: 0, + tickCumulativeOutside: 0, + secondsOutside: 0, + initialized: true, + }) + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, 0, 15, 15) + expect(feeGrowthInside0X128).to.eq(9) + expect(feeGrowthInside1X128).to.eq(11) + }) + + it('works correctly with overflow on inside tick', async () => { + await tickTest.setTick(-2, { + feeGrowthOutside0X128: constants.MaxUint256.sub(3), + feeGrowthOutside1X128: constants.MaxUint256.sub(2), + liquidityGross: 0, + liquidityNet: 0, + secondsPerLiquidityOutsideX128: 0, + tickCumulativeOutside: 0, + secondsOutside: 0, + initialized: true, + }) + await tickTest.setTick(2, { + feeGrowthOutside0X128: 3, + feeGrowthOutside1X128: 5, + liquidityGross: 0, + liquidityNet: 0, + secondsPerLiquidityOutsideX128: 0, + tickCumulativeOutside: 0, + secondsOutside: 0, + initialized: true, + }) + const { feeGrowthInside0X128, feeGrowthInside1X128 } = await tickTest.getFeeGrowthInside(-2, 2, 0, 15, 15) + expect(feeGrowthInside0X128).to.eq(16) + expect(feeGrowthInside1X128).to.eq(13) + }) + }) + + describe('#update', async () => { + it('flips from zero to nonzero', async () => { + expect(await tickTest.callStatic.update(0, 0, 1, 0, 0, 0, 0, 0, false, 3)).to.eq(true) + }) + it('does not flip from nonzero to greater nonzero', async () => { + await tickTest.update(0, 0, 1, 0, 0, 0, 0, 0, false, 3) + expect(await tickTest.callStatic.update(0, 0, 1, 0, 0, 0, 0, 0, false, 3)).to.eq(false) + }) + it('flips from nonzero to zero', async () => { + await tickTest.update(0, 0, 1, 0, 0, 0, 0, 0, false, 3) + expect(await tickTest.callStatic.update(0, 0, -1, 0, 0, 0, 0, 0, false, 3)).to.eq(true) + }) + it('does not flip from nonzero to lesser nonzero', async () => { + await tickTest.update(0, 0, 2, 0, 0, 0, 0, 0, false, 3) + expect(await tickTest.callStatic.update(0, 0, -1, 0, 0, 0, 0, 0, false, 3)).to.eq(false) + }) + it('does not flip from nonzero to lesser nonzero', async () => { + await tickTest.update(0, 0, 2, 0, 0, 0, 0, 0, false, 3) + expect(await tickTest.callStatic.update(0, 0, -1, 0, 0, 0, 0, 0, false, 3)).to.eq(false) + }) + it('reverts if total liquidity gross is greater than max', async () => { + await tickTest.update(0, 0, 2, 0, 0, 0, 0, 0, false, 3) + await tickTest.update(0, 0, 1, 0, 0, 0, 0, 0, true, 3) + await expect(tickTest.update(0, 0, 1, 0, 0, 0, 0, 0, false, 3)).to.be.revertedWith('LO') + }) + it('nets the liquidity based on upper flag', async () => { + await tickTest.update(0, 0, 2, 0, 0, 0, 0, 0, false, 10) + await tickTest.update(0, 0, 1, 0, 0, 0, 0, 0, true, 10) + await tickTest.update(0, 0, 3, 0, 0, 0, 0, 0, true, 10) + await tickTest.update(0, 0, 1, 0, 0, 0, 0, 0, false, 10) + const { liquidityGross, liquidityNet } = await tickTest.ticks(0) + expect(liquidityGross).to.eq(2 + 1 + 3 + 1) + expect(liquidityNet).to.eq(2 - 1 - 3 + 1) + }) + it('reverts on overflow liquidity gross', async () => { + await tickTest.update(0, 0, MaxUint128.div(2).sub(1), 0, 0, 0, 0, 0, false, MaxUint128) + await expect(tickTest.update(0, 0, MaxUint128.div(2).sub(1), 0, 0, 0, 0, 0, false, MaxUint128)).to.be.reverted + }) + it('assumes all growth happens below ticks lte current tick', async () => { + await tickTest.update(1, 1, 1, 1, 2, 3, 4, 5, false, MaxUint128) + const { + feeGrowthOutside0X128, + feeGrowthOutside1X128, + secondsOutside, + secondsPerLiquidityOutsideX128, + tickCumulativeOutside, + initialized, + } = await tickTest.ticks(1) + expect(feeGrowthOutside0X128).to.eq(1) + expect(feeGrowthOutside1X128).to.eq(2) + expect(secondsPerLiquidityOutsideX128).to.eq(3) + expect(tickCumulativeOutside).to.eq(4) + expect(secondsOutside).to.eq(5) + expect(initialized).to.eq(true) + }) + it('does not set any growth fields if tick is already initialized', async () => { + await tickTest.update(1, 1, 1, 1, 2, 3, 4, 5, false, MaxUint128) + await tickTest.update(1, 1, 1, 6, 7, 8, 9, 10, false, MaxUint128) + const { + feeGrowthOutside0X128, + feeGrowthOutside1X128, + secondsOutside, + secondsPerLiquidityOutsideX128, + tickCumulativeOutside, + initialized, + } = await tickTest.ticks(1) + expect(feeGrowthOutside0X128).to.eq(1) + expect(feeGrowthOutside1X128).to.eq(2) + expect(secondsPerLiquidityOutsideX128).to.eq(3) + expect(tickCumulativeOutside).to.eq(4) + expect(secondsOutside).to.eq(5) + expect(initialized).to.eq(true) + }) + it('does not set any growth fields for ticks gt current tick', async () => { + await tickTest.update(2, 1, 1, 1, 2, 3, 4, 5, false, MaxUint128) + const { + feeGrowthOutside0X128, + feeGrowthOutside1X128, + secondsOutside, + secondsPerLiquidityOutsideX128, + tickCumulativeOutside, + initialized, + } = await tickTest.ticks(2) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + expect(secondsPerLiquidityOutsideX128).to.eq(0) + expect(tickCumulativeOutside).to.eq(0) + expect(secondsOutside).to.eq(0) + expect(initialized).to.eq(true) + }) + }) + + // this is skipped because the presence of the method causes slither to fail + describe('#clear', async () => { + it('deletes all the data in the tick', async () => { + await tickTest.setTick(2, { + feeGrowthOutside0X128: 1, + feeGrowthOutside1X128: 2, + liquidityGross: 3, + liquidityNet: 4, + secondsPerLiquidityOutsideX128: 5, + tickCumulativeOutside: 6, + secondsOutside: 7, + initialized: true, + }) + await tickTest.clear(2) + const { + feeGrowthOutside0X128, + feeGrowthOutside1X128, + secondsOutside, + secondsPerLiquidityOutsideX128, + liquidityGross, + tickCumulativeOutside, + liquidityNet, + initialized, + } = await tickTest.ticks(2) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + expect(secondsOutside).to.eq(0) + expect(secondsPerLiquidityOutsideX128).to.eq(0) + expect(tickCumulativeOutside).to.eq(0) + expect(liquidityGross).to.eq(0) + expect(liquidityNet).to.eq(0) + expect(initialized).to.eq(false) + }) + }) + + describe('#cross', () => { + it('flips the growth variables', async () => { + await tickTest.setTick(2, { + feeGrowthOutside0X128: 1, + feeGrowthOutside1X128: 2, + liquidityGross: 3, + liquidityNet: 4, + secondsPerLiquidityOutsideX128: 5, + tickCumulativeOutside: 6, + secondsOutside: 7, + initialized: true, + }) + await tickTest.cross(2, 7, 9, 8, 15, 10) + const { + feeGrowthOutside0X128, + feeGrowthOutside1X128, + secondsOutside, + tickCumulativeOutside, + secondsPerLiquidityOutsideX128, + } = await tickTest.ticks(2) + expect(feeGrowthOutside0X128).to.eq(6) + expect(feeGrowthOutside1X128).to.eq(7) + expect(secondsPerLiquidityOutsideX128).to.eq(3) + expect(tickCumulativeOutside).to.eq(9) + expect(secondsOutside).to.eq(3) + }) + it('two flips are no op', async () => { + await tickTest.setTick(2, { + feeGrowthOutside0X128: 1, + feeGrowthOutside1X128: 2, + liquidityGross: 3, + liquidityNet: 4, + secondsPerLiquidityOutsideX128: 5, + tickCumulativeOutside: 6, + secondsOutside: 7, + initialized: true, + }) + await tickTest.cross(2, 7, 9, 8, 15, 10) + await tickTest.cross(2, 7, 9, 8, 15, 10) + const { + feeGrowthOutside0X128, + feeGrowthOutside1X128, + secondsOutside, + tickCumulativeOutside, + secondsPerLiquidityOutsideX128, + } = await tickTest.ticks(2) + expect(feeGrowthOutside0X128).to.eq(1) + expect(feeGrowthOutside1X128).to.eq(2) + expect(secondsPerLiquidityOutsideX128).to.eq(5) + expect(tickCumulativeOutside).to.eq(6) + expect(secondsOutside).to.eq(7) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/TickBitmap.spec.ts b/lib/uniswap/v3-core/test/TickBitmap.spec.ts new file mode 100644 index 0000000..6c50779 --- /dev/null +++ b/lib/uniswap/v3-core/test/TickBitmap.spec.ts @@ -0,0 +1,227 @@ +import { ethers } from 'hardhat' +import { TickBitmapTest } from '../typechain/TickBitmapTest' +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' + +describe('TickBitmap', () => { + let tickBitmap: TickBitmapTest + + beforeEach('deploy TickBitmapTest', async () => { + const tickBitmapTestFactory = await ethers.getContractFactory('TickBitmapTest') + tickBitmap = (await tickBitmapTestFactory.deploy()) as TickBitmapTest + }) + + async function initTicks(ticks: number[]): Promise { + for (const tick of ticks) { + await tickBitmap.flipTick(tick) + } + } + + describe('#isInitialized', () => { + it('is false at first', async () => { + expect(await tickBitmap.isInitialized(1)).to.eq(false) + }) + it('is flipped by #flipTick', async () => { + await tickBitmap.flipTick(1) + expect(await tickBitmap.isInitialized(1)).to.eq(true) + }) + it('is flipped back by #flipTick', async () => { + await tickBitmap.flipTick(1) + await tickBitmap.flipTick(1) + expect(await tickBitmap.isInitialized(1)).to.eq(false) + }) + it('is not changed by another flip to a different tick', async () => { + await tickBitmap.flipTick(2) + expect(await tickBitmap.isInitialized(1)).to.eq(false) + }) + it('is not changed by another flip to a different tick on another word', async () => { + await tickBitmap.flipTick(1 + 256) + expect(await tickBitmap.isInitialized(257)).to.eq(true) + expect(await tickBitmap.isInitialized(1)).to.eq(false) + }) + }) + + describe('#flipTick', () => { + it('flips only the specified tick', async () => { + await tickBitmap.flipTick(-230) + expect(await tickBitmap.isInitialized(-230)).to.eq(true) + expect(await tickBitmap.isInitialized(-231)).to.eq(false) + expect(await tickBitmap.isInitialized(-229)).to.eq(false) + expect(await tickBitmap.isInitialized(-230 + 256)).to.eq(false) + expect(await tickBitmap.isInitialized(-230 - 256)).to.eq(false) + await tickBitmap.flipTick(-230) + expect(await tickBitmap.isInitialized(-230)).to.eq(false) + expect(await tickBitmap.isInitialized(-231)).to.eq(false) + expect(await tickBitmap.isInitialized(-229)).to.eq(false) + expect(await tickBitmap.isInitialized(-230 + 256)).to.eq(false) + expect(await tickBitmap.isInitialized(-230 - 256)).to.eq(false) + }) + + it('reverts only itself', async () => { + await tickBitmap.flipTick(-230) + await tickBitmap.flipTick(-259) + await tickBitmap.flipTick(-229) + await tickBitmap.flipTick(500) + await tickBitmap.flipTick(-259) + await tickBitmap.flipTick(-229) + await tickBitmap.flipTick(-259) + + expect(await tickBitmap.isInitialized(-259)).to.eq(true) + expect(await tickBitmap.isInitialized(-229)).to.eq(false) + }) + + it('gas cost of flipping first tick in word to initialized', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfFlipTick(1)) + }) + it('gas cost of flipping second tick in word to initialized', async () => { + await tickBitmap.flipTick(0) + await snapshotGasCost(await tickBitmap.getGasCostOfFlipTick(1)) + }) + it('gas cost of flipping a tick that results in deleting a word', async () => { + await tickBitmap.flipTick(0) + await snapshotGasCost(await tickBitmap.getGasCostOfFlipTick(0)) + }) + }) + + describe('#nextInitializedTickWithinOneWord', () => { + beforeEach('set up some ticks', async () => { + // word boundaries are at multiples of 256 + await initTicks([-200, -55, -4, 70, 78, 84, 139, 240, 535]) + }) + + describe('lte = false', async () => { + it('returns tick to right if at initialized tick', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(78, false) + expect(next).to.eq(84) + expect(initialized).to.eq(true) + }) + it('returns tick to right if at initialized tick', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(-55, false) + expect(next).to.eq(-4) + expect(initialized).to.eq(true) + }) + + it('returns the tick directly to the right', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(77, false) + expect(next).to.eq(78) + expect(initialized).to.eq(true) + }) + it('returns the tick directly to the right', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(-56, false) + expect(next).to.eq(-55) + expect(initialized).to.eq(true) + }) + + it('returns the next words initialized tick if on the right boundary', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(255, false) + expect(next).to.eq(511) + expect(initialized).to.eq(false) + }) + it('returns the next words initialized tick if on the right boundary', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(-257, false) + expect(next).to.eq(-200) + expect(initialized).to.eq(true) + }) + + it('returns the next initialized tick from the next word', async () => { + await tickBitmap.flipTick(340) + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(328, false) + expect(next).to.eq(340) + expect(initialized).to.eq(true) + }) + it('does not exceed boundary', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(508, false) + expect(next).to.eq(511) + expect(initialized).to.eq(false) + }) + it('skips entire word', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(255, false) + expect(next).to.eq(511) + expect(initialized).to.eq(false) + }) + it('skips half word', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(383, false) + expect(next).to.eq(511) + expect(initialized).to.eq(false) + }) + + it('gas cost on boundary', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfNextInitializedTickWithinOneWord(255, false)) + }) + it('gas cost just below boundary', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfNextInitializedTickWithinOneWord(254, false)) + }) + it('gas cost for entire word', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfNextInitializedTickWithinOneWord(768, false)) + }) + }) + + describe('lte = true', () => { + it('returns same tick if initialized', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(78, true) + + expect(next).to.eq(78) + expect(initialized).to.eq(true) + }) + it('returns tick directly to the left of input tick if not initialized', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(79, true) + + expect(next).to.eq(78) + expect(initialized).to.eq(true) + }) + it('will not exceed the word boundary', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(258, true) + + expect(next).to.eq(256) + expect(initialized).to.eq(false) + }) + it('at the word boundary', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(256, true) + + expect(next).to.eq(256) + expect(initialized).to.eq(false) + }) + it('word boundary less 1 (next initialized tick in next word)', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(72, true) + + expect(next).to.eq(70) + expect(initialized).to.eq(true) + }) + it('word boundary', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(-257, true) + + expect(next).to.eq(-512) + expect(initialized).to.eq(false) + }) + it('entire empty word', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(1023, true) + + expect(next).to.eq(768) + expect(initialized).to.eq(false) + }) + it('halfway through empty word', async () => { + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(900, true) + + expect(next).to.eq(768) + expect(initialized).to.eq(false) + }) + it('boundary is initialized', async () => { + await tickBitmap.flipTick(329) + const { next, initialized } = await tickBitmap.nextInitializedTickWithinOneWord(456, true) + + expect(next).to.eq(329) + expect(initialized).to.eq(true) + }) + + it('gas cost on boundary', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfNextInitializedTickWithinOneWord(256, true)) + }) + it('gas cost just below boundary', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfNextInitializedTickWithinOneWord(255, true)) + }) + it('gas cost for entire word', async () => { + await snapshotGasCost(await tickBitmap.getGasCostOfNextInitializedTickWithinOneWord(1024, true)) + }) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/TickMath.spec.ts b/lib/uniswap/v3-core/test/TickMath.spec.ts new file mode 100644 index 0000000..0f5f5bb --- /dev/null +++ b/lib/uniswap/v3-core/test/TickMath.spec.ts @@ -0,0 +1,153 @@ +import { BigNumber } from 'ethers' +import { ethers } from 'hardhat' +import { TickMathTest } from '../typechain/TickMathTest' +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' +import { encodePriceSqrt, MIN_SQRT_RATIO, MAX_SQRT_RATIO } from './shared/utilities' +import Decimal from 'decimal.js' + +const MIN_TICK = -887272 +const MAX_TICK = 887272 + +Decimal.config({ toExpNeg: -500, toExpPos: 500 }) + +describe('TickMath', () => { + let tickMath: TickMathTest + + before('deploy TickMathTest', async () => { + const factory = await ethers.getContractFactory('TickMathTest') + tickMath = (await factory.deploy()) as TickMathTest + }) + + describe('#getSqrtRatioAtTick', () => { + it('throws for too low', async () => { + await expect(tickMath.getSqrtRatioAtTick(MIN_TICK - 1)).to.be.revertedWith('T') + }) + + it('throws for too low', async () => { + await expect(tickMath.getSqrtRatioAtTick(MAX_TICK + 1)).to.be.revertedWith('T') + }) + + it('min tick', async () => { + expect(await tickMath.getSqrtRatioAtTick(MIN_TICK)).to.eq('4295128739') + }) + + it('min tick +1', async () => { + expect(await tickMath.getSqrtRatioAtTick(MIN_TICK + 1)).to.eq('4295343490') + }) + + it('max tick - 1', async () => { + expect(await tickMath.getSqrtRatioAtTick(MAX_TICK - 1)).to.eq('1461373636630004318706518188784493106690254656249') + }) + + it('min tick ratio is less than js implementation', async () => { + expect(await tickMath.getSqrtRatioAtTick(MIN_TICK)).to.be.lt(encodePriceSqrt(1, BigNumber.from(2).pow(127))) + }) + + it('max tick ratio is greater than js implementation', async () => { + expect(await tickMath.getSqrtRatioAtTick(MAX_TICK)).to.be.gt(encodePriceSqrt(BigNumber.from(2).pow(127), 1)) + }) + + it('max tick', async () => { + expect(await tickMath.getSqrtRatioAtTick(MAX_TICK)).to.eq('1461446703485210103287273052203988822378723970342') + }) + + for (const absTick of [ + 50, 100, 250, 500, 1_000, 2_500, 3_000, 4_000, 5_000, 50_000, 150_000, 250_000, 500_000, 738_203, + ]) { + for (const tick of [-absTick, absTick]) { + describe(`tick ${tick}`, () => { + it('is at most off by 1/100th of a bips', async () => { + const jsResult = new Decimal(1.0001).pow(tick).sqrt().mul(new Decimal(2).pow(96)) + const result = await tickMath.getSqrtRatioAtTick(tick) + const absDiff = new Decimal(result.toString()).sub(jsResult).abs() + expect(absDiff.div(jsResult).toNumber()).to.be.lt(0.000001) + }) + it('result', async () => { + expect((await tickMath.getSqrtRatioAtTick(tick)).toString()).to.matchSnapshot() + }) + it('gas', async () => { + await snapshotGasCost(tickMath.getGasCostOfGetSqrtRatioAtTick(tick)) + }) + }) + } + } + }) + + describe('#MIN_SQRT_RATIO', async () => { + it('equals #getSqrtRatioAtTick(MIN_TICK)', async () => { + const min = await tickMath.getSqrtRatioAtTick(MIN_TICK) + expect(min).to.eq(await tickMath.MIN_SQRT_RATIO()) + expect(min).to.eq(MIN_SQRT_RATIO) + }) + }) + + describe('#MAX_SQRT_RATIO', async () => { + it('equals #getSqrtRatioAtTick(MAX_TICK)', async () => { + const max = await tickMath.getSqrtRatioAtTick(MAX_TICK) + expect(max).to.eq(await tickMath.MAX_SQRT_RATIO()) + expect(max).to.eq(MAX_SQRT_RATIO) + }) + }) + + describe('#getTickAtSqrtRatio', () => { + it('throws for too low', async () => { + await expect(tickMath.getTickAtSqrtRatio(MIN_SQRT_RATIO.sub(1))).to.be.revertedWith('R') + }) + + it('throws for too high', async () => { + await expect(tickMath.getTickAtSqrtRatio(BigNumber.from(MAX_SQRT_RATIO))).to.be.revertedWith('R') + }) + + it('ratio of min tick', async () => { + expect(await tickMath.getTickAtSqrtRatio(MIN_SQRT_RATIO)).to.eq(MIN_TICK) + }) + it('ratio of min tick + 1', async () => { + expect(await tickMath.getTickAtSqrtRatio('4295343490')).to.eq(MIN_TICK + 1) + }) + it('ratio of max tick - 1', async () => { + expect(await tickMath.getTickAtSqrtRatio('1461373636630004318706518188784493106690254656249')).to.eq(MAX_TICK - 1) + }) + it('ratio closest to max tick', async () => { + expect(await tickMath.getTickAtSqrtRatio(MAX_SQRT_RATIO.sub(1))).to.eq(MAX_TICK - 1) + }) + + for (const ratio of [ + MIN_SQRT_RATIO, + encodePriceSqrt(BigNumber.from(10).pow(12), 1), + encodePriceSqrt(BigNumber.from(10).pow(6), 1), + encodePriceSqrt(1, 64), + encodePriceSqrt(1, 8), + encodePriceSqrt(1, 2), + encodePriceSqrt(1, 1), + encodePriceSqrt(2, 1), + encodePriceSqrt(8, 1), + encodePriceSqrt(64, 1), + encodePriceSqrt(1, BigNumber.from(10).pow(6)), + encodePriceSqrt(1, BigNumber.from(10).pow(12)), + MAX_SQRT_RATIO.sub(1), + ]) { + describe(`ratio ${ratio}`, () => { + it('is at most off by 1', async () => { + const jsResult = new Decimal(ratio.toString()).div(new Decimal(2).pow(96)).pow(2).log(1.0001).floor() + const result = await tickMath.getTickAtSqrtRatio(ratio) + const absDiff = new Decimal(result.toString()).sub(jsResult).abs() + expect(absDiff.toNumber()).to.be.lte(1) + }) + it('ratio is between the tick and tick+1', async () => { + const tick = await tickMath.getTickAtSqrtRatio(ratio) + const ratioOfTick = await tickMath.getSqrtRatioAtTick(tick) + const ratioOfTickPlusOne = await tickMath.getSqrtRatioAtTick(tick + 1) + expect(ratio).to.be.gte(ratioOfTick) + expect(ratio).to.be.lt(ratioOfTickPlusOne) + }) + it('result', async () => { + expect(await tickMath.getTickAtSqrtRatio(ratio)).to.matchSnapshot() + }) + it('gas', async () => { + await snapshotGasCost(tickMath.getGasCostOfGetTickAtSqrtRatio(ratio)) + }) + }) + } + }) +}) diff --git a/lib/uniswap/v3-core/test/UniswapV3Factory.spec.ts b/lib/uniswap/v3-core/test/UniswapV3Factory.spec.ts new file mode 100644 index 0000000..afdc922 --- /dev/null +++ b/lib/uniswap/v3-core/test/UniswapV3Factory.spec.ts @@ -0,0 +1,177 @@ +import { Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { UniswapV3Factory } from '../typechain/UniswapV3Factory' +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' + +import { FeeAmount, getCreate2Address, TICK_SPACINGS } from './shared/utilities' + +const { constants } = ethers + +const TEST_ADDRESSES: [string, string] = [ + '0x1000000000000000000000000000000000000000', + '0x2000000000000000000000000000000000000000', +] + +const createFixtureLoader = waffle.createFixtureLoader + +describe('UniswapV3Factory', () => { + let wallet: Wallet, other: Wallet + + let factory: UniswapV3Factory + let poolBytecode: string + const fixture = async () => { + const factoryFactory = await ethers.getContractFactory('UniswapV3Factory') + return (await factoryFactory.deploy()) as UniswapV3Factory + } + + let loadFixture: ReturnType + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + + loadFixture = createFixtureLoader([wallet, other]) + }) + + before('load pool bytecode', async () => { + poolBytecode = (await ethers.getContractFactory('UniswapV3Pool')).bytecode + }) + + beforeEach('deploy factory', async () => { + factory = await loadFixture(fixture) + }) + + it('owner is deployer', async () => { + expect(await factory.owner()).to.eq(wallet.address) + }) + + it('factory bytecode size', async () => { + expect(((await waffle.provider.getCode(factory.address)).length - 2) / 2).to.matchSnapshot() + }) + + it('pool bytecode size', async () => { + await factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], FeeAmount.MEDIUM) + const poolAddress = getCreate2Address(factory.address, TEST_ADDRESSES, FeeAmount.MEDIUM, poolBytecode) + expect(((await waffle.provider.getCode(poolAddress)).length - 2) / 2).to.matchSnapshot() + }) + + it('initial enabled fee amounts', async () => { + expect(await factory.feeAmountTickSpacing(FeeAmount.LOW)).to.eq(TICK_SPACINGS[FeeAmount.LOW]) + expect(await factory.feeAmountTickSpacing(FeeAmount.MEDIUM)).to.eq(TICK_SPACINGS[FeeAmount.MEDIUM]) + expect(await factory.feeAmountTickSpacing(FeeAmount.HIGH)).to.eq(TICK_SPACINGS[FeeAmount.HIGH]) + }) + + async function createAndCheckPool( + tokens: [string, string], + feeAmount: FeeAmount, + tickSpacing: number = TICK_SPACINGS[feeAmount] + ) { + const create2Address = getCreate2Address(factory.address, tokens, feeAmount, poolBytecode) + const create = factory.createPool(tokens[0], tokens[1], feeAmount) + + await expect(create) + .to.emit(factory, 'PoolCreated') + .withArgs(TEST_ADDRESSES[0], TEST_ADDRESSES[1], feeAmount, tickSpacing, create2Address) + + await expect(factory.createPool(tokens[0], tokens[1], feeAmount)).to.be.reverted + await expect(factory.createPool(tokens[1], tokens[0], feeAmount)).to.be.reverted + expect(await factory.getPool(tokens[0], tokens[1], feeAmount), 'getPool in order').to.eq(create2Address) + expect(await factory.getPool(tokens[1], tokens[0], feeAmount), 'getPool in reverse').to.eq(create2Address) + + const poolContractFactory = await ethers.getContractFactory('UniswapV3Pool') + const pool = poolContractFactory.attach(create2Address) + expect(await pool.factory(), 'pool factory address').to.eq(factory.address) + expect(await pool.token0(), 'pool token0').to.eq(TEST_ADDRESSES[0]) + expect(await pool.token1(), 'pool token1').to.eq(TEST_ADDRESSES[1]) + expect(await pool.fee(), 'pool fee').to.eq(feeAmount) + expect(await pool.tickSpacing(), 'pool tick spacing').to.eq(tickSpacing) + } + + describe('#createPool', () => { + it('succeeds for low fee pool', async () => { + await createAndCheckPool(TEST_ADDRESSES, FeeAmount.LOW) + }) + + it('succeeds for medium fee pool', async () => { + await createAndCheckPool(TEST_ADDRESSES, FeeAmount.MEDIUM) + }) + it('succeeds for high fee pool', async () => { + await createAndCheckPool(TEST_ADDRESSES, FeeAmount.HIGH) + }) + + it('succeeds if tokens are passed in reverse', async () => { + await createAndCheckPool([TEST_ADDRESSES[1], TEST_ADDRESSES[0]], FeeAmount.MEDIUM) + }) + + it('fails if token a == token b', async () => { + await expect(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[0], FeeAmount.LOW)).to.be.reverted + }) + + it('fails if token a is 0 or token b is 0', async () => { + await expect(factory.createPool(TEST_ADDRESSES[0], constants.AddressZero, FeeAmount.LOW)).to.be.reverted + await expect(factory.createPool(constants.AddressZero, TEST_ADDRESSES[0], FeeAmount.LOW)).to.be.reverted + await expect(factory.createPool(constants.AddressZero, constants.AddressZero, FeeAmount.LOW)).to.be.revertedWith( + '' + ) + }) + + it('fails if fee amount is not enabled', async () => { + await expect(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], 250)).to.be.reverted + }) + + it('gas', async () => { + await snapshotGasCost(factory.createPool(TEST_ADDRESSES[0], TEST_ADDRESSES[1], FeeAmount.MEDIUM)) + }) + }) + + describe('#setOwner', () => { + it('fails if caller is not owner', async () => { + await expect(factory.connect(other).setOwner(wallet.address)).to.be.reverted + }) + + it('updates owner', async () => { + await factory.setOwner(other.address) + expect(await factory.owner()).to.eq(other.address) + }) + + it('emits event', async () => { + await expect(factory.setOwner(other.address)) + .to.emit(factory, 'OwnerChanged') + .withArgs(wallet.address, other.address) + }) + + it('cannot be called by original owner', async () => { + await factory.setOwner(other.address) + await expect(factory.setOwner(wallet.address)).to.be.reverted + }) + }) + + describe('#enableFeeAmount', () => { + it('fails if caller is not owner', async () => { + await expect(factory.connect(other).enableFeeAmount(100, 2)).to.be.reverted + }) + it('fails if fee is too great', async () => { + await expect(factory.enableFeeAmount(1000000, 10)).to.be.reverted + }) + it('fails if tick spacing is too small', async () => { + await expect(factory.enableFeeAmount(500, 0)).to.be.reverted + }) + it('fails if tick spacing is too large', async () => { + await expect(factory.enableFeeAmount(500, 16834)).to.be.reverted + }) + it('fails if already initialized', async () => { + await factory.enableFeeAmount(100, 5) + await expect(factory.enableFeeAmount(100, 10)).to.be.reverted + }) + it('sets the fee amount in the mapping', async () => { + await factory.enableFeeAmount(100, 5) + expect(await factory.feeAmountTickSpacing(100)).to.eq(5) + }) + it('emits an event', async () => { + await expect(factory.enableFeeAmount(100, 5)).to.emit(factory, 'FeeAmountEnabled').withArgs(100, 5) + }) + it('enables pool creation', async () => { + await factory.enableFeeAmount(250, 15) + await createAndCheckPool([TEST_ADDRESSES[0], TEST_ADDRESSES[1]], 250, 15) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/UniswapV3Pool.arbitrage.spec.ts b/lib/uniswap/v3-core/test/UniswapV3Pool.arbitrage.spec.ts new file mode 100644 index 0000000..2bd9999 --- /dev/null +++ b/lib/uniswap/v3-core/test/UniswapV3Pool.arbitrage.spec.ts @@ -0,0 +1,358 @@ +import Decimal from 'decimal.js' +import { BigNumber, BigNumberish, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { MockTimeUniswapV3Pool } from '../typechain/MockTimeUniswapV3Pool' +import { TickMathTest } from '../typechain/TickMathTest' +import { UniswapV3PoolSwapTest } from '../typechain/UniswapV3PoolSwapTest' +import { expect } from './shared/expect' + +import { poolFixture } from './shared/fixtures' +import { formatPrice, formatTokenAmount } from './shared/format' + +import { + createPoolFunctions, + encodePriceSqrt, + expandTo18Decimals, + FeeAmount, + getMaxLiquidityPerTick, + getMaxTick, + getMinTick, + MAX_SQRT_RATIO, + MaxUint128, + MIN_SQRT_RATIO, + MintFunction, + SwapFunction, + TICK_SPACINGS, +} from './shared/utilities' + +const { + constants: { MaxUint256 }, +} = ethers + +const createFixtureLoader = waffle.createFixtureLoader + +Decimal.config({ toExpNeg: -500, toExpPos: 500 }) + +function applySqrtRatioBipsHundredthsDelta(sqrtRatio: BigNumber, bipsHundredths: number): BigNumber { + return BigNumber.from( + new Decimal( + sqrtRatio + .mul(sqrtRatio) + .mul(1e6 + bipsHundredths) + .div(1e6) + .toString() + ) + .sqrt() + .floor() + .toString() + ) +} + +describe('UniswapV3Pool arbitrage tests', () => { + let wallet: Wallet, arbitrageur: Wallet + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + ;[wallet, arbitrageur] = await (ethers as any).getSigners() + loadFixture = createFixtureLoader([wallet, arbitrageur]) + }) + + for (const feeProtocol of [0, 6]) { + describe(`protocol fee = ${feeProtocol};`, () => { + const startingPrice = encodePriceSqrt(1, 1) + const startingTick = 0 + const feeAmount = FeeAmount.MEDIUM + const tickSpacing = TICK_SPACINGS[feeAmount] + const minTick = getMinTick(tickSpacing) + const maxTick = getMaxTick(tickSpacing) + + for (const passiveLiquidity of [ + expandTo18Decimals(1).div(100), + expandTo18Decimals(1), + expandTo18Decimals(10), + expandTo18Decimals(100), + ]) { + describe(`passive liquidity of ${formatTokenAmount(passiveLiquidity)}`, () => { + const arbTestFixture = async ([wallet, arbitrageur]: Wallet[]) => { + const fix = await poolFixture([wallet], waffle.provider) + + const pool = await fix.createPool(feeAmount, tickSpacing) + + await fix.token0.transfer(arbitrageur.address, BigNumber.from(2).pow(254)) + await fix.token1.transfer(arbitrageur.address, BigNumber.from(2).pow(254)) + + const { swapExact0For1, swapToHigherPrice, swapToLowerPrice, swapExact1For0, mint } = + await createPoolFunctions({ + swapTarget: fix.swapTargetCallee, + token0: fix.token0, + token1: fix.token1, + pool, + }) + + const testerFactory = await ethers.getContractFactory('UniswapV3PoolSwapTest') + const tester = (await testerFactory.deploy()) as UniswapV3PoolSwapTest + + const tickMathFactory = await ethers.getContractFactory('TickMathTest') + const tickMath = (await tickMathFactory.deploy()) as TickMathTest + + await fix.token0.approve(tester.address, MaxUint256) + await fix.token1.approve(tester.address, MaxUint256) + + await pool.initialize(startingPrice) + if (feeProtocol != 0) await pool.setFeeProtocol(feeProtocol, feeProtocol) + await mint(wallet.address, minTick, maxTick, passiveLiquidity) + + expect((await pool.slot0()).tick).to.eq(startingTick) + expect((await pool.slot0()).sqrtPriceX96).to.eq(startingPrice) + + return { pool, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice, swapExact1For0, tester, tickMath } + } + + let swapExact0For1: SwapFunction + let swapToHigherPrice: SwapFunction + let swapToLowerPrice: SwapFunction + let swapExact1For0: SwapFunction + let pool: MockTimeUniswapV3Pool + let mint: MintFunction + let tester: UniswapV3PoolSwapTest + let tickMath: TickMathTest + + beforeEach('load the fixture', async () => { + ;({ swapExact0For1, pool, mint, swapToHigherPrice, swapToLowerPrice, swapExact1For0, tester, tickMath } = + await loadFixture(arbTestFixture)) + }) + + async function simulateSwap( + zeroForOne: boolean, + amountSpecified: BigNumberish, + sqrtPriceLimitX96?: BigNumber + ): Promise<{ + executionPrice: BigNumber + nextSqrtRatio: BigNumber + amount0Delta: BigNumber + amount1Delta: BigNumber + }> { + const { amount0Delta, amount1Delta, nextSqrtRatio } = await tester.callStatic.getSwapResult( + pool.address, + zeroForOne, + amountSpecified, + sqrtPriceLimitX96 ?? (zeroForOne ? MIN_SQRT_RATIO.add(1) : MAX_SQRT_RATIO.sub(1)) + ) + + const executionPrice = zeroForOne + ? encodePriceSqrt(amount1Delta, amount0Delta.mul(-1)) + : encodePriceSqrt(amount1Delta.mul(-1), amount0Delta) + + return { executionPrice, nextSqrtRatio, amount0Delta, amount1Delta } + } + + for (const { zeroForOne, assumedTruePriceAfterSwap, inputAmount, description } of [ + { + description: 'exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98', + zeroForOne: true, + inputAmount: expandTo18Decimals(10), + assumedTruePriceAfterSwap: encodePriceSqrt(98, 100), + }, + { + description: 'exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01', + zeroForOne: true, + inputAmount: expandTo18Decimals(10), + assumedTruePriceAfterSwap: encodePriceSqrt(101, 100), + }, + ]) { + describe(description, () => { + function valueToken1(arbBalance0: BigNumber, arbBalance1: BigNumber) { + return assumedTruePriceAfterSwap + .mul(assumedTruePriceAfterSwap) + .mul(arbBalance0) + .div(BigNumber.from(2).pow(192)) + .add(arbBalance1) + } + + it('not sandwiched', async () => { + const { executionPrice, amount1Delta, amount0Delta } = await simulateSwap(zeroForOne, inputAmount) + zeroForOne + ? await swapExact0For1(inputAmount, wallet.address) + : await swapExact1For0(inputAmount, wallet.address) + + expect({ + executionPrice: formatPrice(executionPrice), + amount0Delta: formatTokenAmount(amount0Delta), + amount1Delta: formatTokenAmount(amount1Delta), + priceAfter: formatPrice((await pool.slot0()).sqrtPriceX96), + }).to.matchSnapshot() + }) + + it('sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity', async () => { + const { executionPrice } = await simulateSwap(zeroForOne, inputAmount) + + const firstTickAboveMarginalPrice = zeroForOne + ? Math.ceil( + (await tickMath.getTickAtSqrtRatio( + applySqrtRatioBipsHundredthsDelta(executionPrice, feeAmount) + )) / tickSpacing + ) * tickSpacing + : Math.floor( + (await tickMath.getTickAtSqrtRatio( + applySqrtRatioBipsHundredthsDelta(executionPrice, -feeAmount) + )) / tickSpacing + ) * tickSpacing + const tickAfterFirstTickAboveMarginPrice = zeroForOne + ? firstTickAboveMarginalPrice - tickSpacing + : firstTickAboveMarginalPrice + tickSpacing + + const priceSwapStart = await tickMath.getSqrtRatioAtTick(firstTickAboveMarginalPrice) + + let arbBalance0 = BigNumber.from(0) + let arbBalance1 = BigNumber.from(0) + + // first frontrun to the first tick before the execution price + const { + amount0Delta: frontrunDelta0, + amount1Delta: frontrunDelta1, + executionPrice: frontrunExecutionPrice, + } = await simulateSwap(zeroForOne, MaxUint256.div(2), priceSwapStart) + arbBalance0 = arbBalance0.sub(frontrunDelta0) + arbBalance1 = arbBalance1.sub(frontrunDelta1) + zeroForOne + ? await swapToLowerPrice(priceSwapStart, arbitrageur.address) + : await swapToHigherPrice(priceSwapStart, arbitrageur.address) + + const profitToken1AfterFrontRun = valueToken1(arbBalance0, arbBalance1) + + const tickLower = zeroForOne ? tickAfterFirstTickAboveMarginPrice : firstTickAboveMarginalPrice + const tickUpper = zeroForOne ? firstTickAboveMarginalPrice : tickAfterFirstTickAboveMarginPrice + + // deposit max liquidity at the tick + const mintReceipt = await ( + await mint(wallet.address, tickLower, tickUpper, getMaxLiquidityPerTick(tickSpacing)) + ).wait() + // sub the mint costs + const { amount0: amount0Mint, amount1: amount1Mint } = pool.interface.decodeEventLog( + pool.interface.events['Mint(address,address,int24,int24,uint128,uint256,uint256)'], + mintReceipt.events?.[2].data! + ) + arbBalance0 = arbBalance0.sub(amount0Mint) + arbBalance1 = arbBalance1.sub(amount1Mint) + + // execute the user's swap + const { executionPrice: executionPriceAfterFrontrun } = await simulateSwap(zeroForOne, inputAmount) + zeroForOne + ? await swapExact0For1(inputAmount, wallet.address) + : await swapExact1For0(inputAmount, wallet.address) + + // burn the arb's liquidity + const { amount0: amount0Burn, amount1: amount1Burn } = await pool.callStatic.burn( + tickLower, + tickUpper, + getMaxLiquidityPerTick(tickSpacing) + ) + await pool.burn(tickLower, tickUpper, getMaxLiquidityPerTick(tickSpacing)) + arbBalance0 = arbBalance0.add(amount0Burn) + arbBalance1 = arbBalance1.add(amount1Burn) + + // add the fees as well + const { amount0: amount0CollectAndBurn, amount1: amount1CollectAndBurn } = + await pool.callStatic.collect(arbitrageur.address, tickLower, tickUpper, MaxUint128, MaxUint128) + const [amount0Collect, amount1Collect] = [ + amount0CollectAndBurn.sub(amount0Burn), + amount1CollectAndBurn.sub(amount1Burn), + ] + arbBalance0 = arbBalance0.add(amount0Collect) + arbBalance1 = arbBalance1.add(amount1Collect) + + const profitToken1AfterSandwich = valueToken1(arbBalance0, arbBalance1) + + // backrun the swap to true price, i.e. swap to the marginal price = true price + const priceToSwapTo = zeroForOne + ? applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, -feeAmount) + : applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, feeAmount) + const { + amount0Delta: backrunDelta0, + amount1Delta: backrunDelta1, + executionPrice: backrunExecutionPrice, + } = await simulateSwap(!zeroForOne, MaxUint256.div(2), priceToSwapTo) + await swapToHigherPrice(priceToSwapTo, wallet.address) + arbBalance0 = arbBalance0.sub(backrunDelta0) + arbBalance1 = arbBalance1.sub(backrunDelta1) + + expect({ + sandwichedPrice: formatPrice(executionPriceAfterFrontrun), + arbBalanceDelta0: formatTokenAmount(arbBalance0), + arbBalanceDelta1: formatTokenAmount(arbBalance1), + profit: { + final: formatTokenAmount(valueToken1(arbBalance0, arbBalance1)), + afterFrontrun: formatTokenAmount(profitToken1AfterFrontRun), + afterSandwich: formatTokenAmount(profitToken1AfterSandwich), + }, + backrun: { + executionPrice: formatPrice(backrunExecutionPrice), + delta0: formatTokenAmount(backrunDelta0), + delta1: formatTokenAmount(backrunDelta1), + }, + frontrun: { + executionPrice: formatPrice(frontrunExecutionPrice), + delta0: formatTokenAmount(frontrunDelta0), + delta1: formatTokenAmount(frontrunDelta1), + }, + collect: { + amount0: formatTokenAmount(amount0Collect), + amount1: formatTokenAmount(amount1Collect), + }, + burn: { + amount0: formatTokenAmount(amount0Burn), + amount1: formatTokenAmount(amount1Burn), + }, + mint: { + amount0: formatTokenAmount(amount0Mint), + amount1: formatTokenAmount(amount1Mint), + }, + finalPrice: formatPrice((await pool.slot0()).sqrtPriceX96), + }).to.matchSnapshot() + }) + + it('backrun to true price after swap only', async () => { + let arbBalance0 = BigNumber.from(0) + let arbBalance1 = BigNumber.from(0) + + zeroForOne + ? await swapExact0For1(inputAmount, wallet.address) + : await swapExact1For0(inputAmount, wallet.address) + + // swap to the marginal price = true price + const priceToSwapTo = zeroForOne + ? applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, -feeAmount) + : applySqrtRatioBipsHundredthsDelta(assumedTruePriceAfterSwap, feeAmount) + const { + amount0Delta: backrunDelta0, + amount1Delta: backrunDelta1, + executionPrice: backrunExecutionPrice, + } = await simulateSwap(!zeroForOne, MaxUint256.div(2), priceToSwapTo) + zeroForOne + ? await swapToHigherPrice(priceToSwapTo, wallet.address) + : await swapToLowerPrice(priceToSwapTo, wallet.address) + arbBalance0 = arbBalance0.sub(backrunDelta0) + arbBalance1 = arbBalance1.sub(backrunDelta1) + + expect({ + arbBalanceDelta0: formatTokenAmount(arbBalance0), + arbBalanceDelta1: formatTokenAmount(arbBalance1), + profit: { + final: formatTokenAmount(valueToken1(arbBalance0, arbBalance1)), + }, + backrun: { + executionPrice: formatPrice(backrunExecutionPrice), + delta0: formatTokenAmount(backrunDelta0), + delta1: formatTokenAmount(backrunDelta1), + }, + finalPrice: formatPrice((await pool.slot0()).sqrtPriceX96), + }).to.matchSnapshot() + }) + }) + } + }) + } + }) + } +}) diff --git a/lib/uniswap/v3-core/test/UniswapV3Pool.gas.spec.ts b/lib/uniswap/v3-core/test/UniswapV3Pool.gas.spec.ts new file mode 100644 index 0000000..877c181 --- /dev/null +++ b/lib/uniswap/v3-core/test/UniswapV3Pool.gas.spec.ts @@ -0,0 +1,315 @@ +import { ethers, waffle } from 'hardhat' +import { Wallet } from 'ethers' +import { MockTimeUniswapV3Pool } from '../typechain/MockTimeUniswapV3Pool' +import { expect } from './shared/expect' + +import { poolFixture } from './shared/fixtures' +import snapshotGasCost from './shared/snapshotGasCost' + +import { + expandTo18Decimals, + FeeAmount, + getMinTick, + encodePriceSqrt, + TICK_SPACINGS, + createPoolFunctions, + SwapFunction, + MintFunction, + getMaxTick, + MaxUint128, + SwapToPriceFunction, + MAX_SQRT_RATIO, + MIN_SQRT_RATIO, +} from './shared/utilities' + +const createFixtureLoader = waffle.createFixtureLoader + +describe('UniswapV3Pool gas tests', () => { + let wallet: Wallet, other: Wallet + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + loadFixture = createFixtureLoader([wallet, other]) + }) + + for (const feeProtocol of [0, 6]) { + describe(feeProtocol > 0 ? 'fee is on' : 'fee is off', () => { + const startingPrice = encodePriceSqrt(100001, 100000) + const startingTick = 0 + const feeAmount = FeeAmount.MEDIUM + const tickSpacing = TICK_SPACINGS[feeAmount] + const minTick = getMinTick(tickSpacing) + const maxTick = getMaxTick(tickSpacing) + + const gasTestFixture = async ([wallet]: Wallet[]) => { + const fix = await poolFixture([wallet], waffle.provider) + + const pool = await fix.createPool(feeAmount, tickSpacing) + + const { swapExact0For1, swapToHigherPrice, mint, swapToLowerPrice } = await createPoolFunctions({ + swapTarget: fix.swapTargetCallee, + token0: fix.token0, + token1: fix.token1, + pool, + }) + + await pool.initialize(encodePriceSqrt(1, 1)) + await pool.setFeeProtocol(feeProtocol, feeProtocol) + await pool.increaseObservationCardinalityNext(4) + await pool.advanceTime(1) + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(2)) + + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await pool.advanceTime(1) + await swapToHigherPrice(startingPrice, wallet.address) + await pool.advanceTime(1) + expect((await pool.slot0()).tick).to.eq(startingTick) + expect((await pool.slot0()).sqrtPriceX96).to.eq(startingPrice) + + return { pool, swapExact0For1, mint, swapToHigherPrice, swapToLowerPrice } + } + + let swapExact0For1: SwapFunction + let swapToHigherPrice: SwapToPriceFunction + let swapToLowerPrice: SwapToPriceFunction + let pool: MockTimeUniswapV3Pool + let mint: MintFunction + + beforeEach('load the fixture', async () => { + ;({ swapExact0For1, pool, mint, swapToHigherPrice, swapToLowerPrice } = await loadFixture(gasTestFixture)) + }) + + describe('#swapExact0For1', () => { + it('first swap in block with no tick movement', async () => { + await snapshotGasCost(swapExact0For1(2000, wallet.address)) + expect((await pool.slot0()).sqrtPriceX96).to.not.eq(startingPrice) + expect((await pool.slot0()).tick).to.eq(startingTick) + }) + + it('first swap in block moves tick, no initialized crossings', async () => { + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)) + expect((await pool.slot0()).tick).to.eq(startingTick - 1) + }) + + it('second swap in block with no tick movement', async () => { + await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) + expect((await pool.slot0()).tick).to.eq(startingTick - 1) + await snapshotGasCost(swapExact0For1(2000, wallet.address)) + expect((await pool.slot0()).tick).to.eq(startingTick - 1) + }) + + it('second swap in block moves tick, no initialized crossings', async () => { + await swapExact0For1(1000, wallet.address) + expect((await pool.slot0()).tick).to.eq(startingTick) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address)) + expect((await pool.slot0()).tick).to.eq(startingTick - 1) + }) + + it('first swap in block, large swap, no initialized crossings', async () => { + await snapshotGasCost(swapExact0For1(expandTo18Decimals(10), wallet.address)) + expect((await pool.slot0()).tick).to.eq(-35787) + }) + + it('first swap in block, large swap crossing several initialized ticks', async () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + expect((await pool.slot0()).tick).to.eq(startingTick) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) // we crossed the last tick + }) + + it('first swap in block, large swap crossing a single initialized tick', async () => { + await mint(wallet.address, minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1)) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await pool.slot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick + }) + + it('second swap in block, large swap crossing several initialized ticks', async () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) + }) + + it('second swap in block, large swap crossing a single initialized tick', async () => { + await mint(wallet.address, minTick, startingTick - 2 * tickSpacing, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1).div(10000), wallet.address) + expect((await pool.slot0()).tick).to.be.gt(startingTick - 2 * tickSpacing) // we didn't cross the initialized tick + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await pool.slot0()).tick).to.be.lt(startingTick - 2 * tickSpacing) // we crossed the last tick + }) + + it('large swap crossing several initialized ticks after some time passes', async () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + await swapExact0For1(2, wallet.address) + await pool.advanceTime(1) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await pool.slot0()).tick).to.be.lt(startingTick - 4 * tickSpacing) + }) + + it('large swap crossing several initialized ticks second time after some time passes', async () => { + await mint(wallet.address, startingTick - 3 * tickSpacing, startingTick - tickSpacing, expandTo18Decimals(1)) + await mint( + wallet.address, + startingTick - 4 * tickSpacing, + startingTick - 2 * tickSpacing, + expandTo18Decimals(1) + ) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await swapToHigherPrice(startingPrice, wallet.address) + await pool.advanceTime(1) + await snapshotGasCost(swapExact0For1(expandTo18Decimals(1), wallet.address)) + expect((await pool.slot0()).tick).to.be.lt(tickSpacing * -4) + }) + }) + + describe('#mint', () => { + for (const { description, tickLower, tickUpper } of [ + { + description: 'around current price', + tickLower: startingTick - tickSpacing, + tickUpper: startingTick + tickSpacing, + }, + { + description: 'below current price', + tickLower: startingTick - 2 * tickSpacing, + tickUpper: startingTick - tickSpacing, + }, + { + description: 'above current price', + tickLower: startingTick + tickSpacing, + tickUpper: startingTick + 2 * tickSpacing, + }, + ]) { + describe(description, () => { + it('new position mint first in range', async () => { + await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('add to position existing', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('second position in same range', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await snapshotGasCost(mint(other.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('add to position after some time passes', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await pool.advanceTime(1) + await snapshotGasCost(mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1))) + }) + }) + } + }) + + describe('#burn', () => { + for (const { description, tickLower, tickUpper } of [ + { + description: 'around current price', + tickLower: startingTick - tickSpacing, + tickUpper: startingTick + tickSpacing, + }, + { + description: 'below current price', + tickLower: startingTick - 2 * tickSpacing, + tickUpper: startingTick - tickSpacing, + }, + { + description: 'above current price', + tickLower: startingTick + tickSpacing, + tickUpper: startingTick + 2 * tickSpacing, + }, + ]) { + describe(description, () => { + const liquidityAmount = expandTo18Decimals(1) + beforeEach('mint a position', async () => { + await mint(wallet.address, tickLower, tickUpper, liquidityAmount) + }) + + it('burn when only position using ticks', async () => { + await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('partial position burn', async () => { + await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1).div(2))) + }) + it('entire position burn but other positions are using the ticks', async () => { + await mint(other.address, tickLower, tickUpper, expandTo18Decimals(1)) + await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) + }) + it('burn entire position after some time passes', async () => { + await pool.advanceTime(1) + await snapshotGasCost(pool.burn(tickLower, tickUpper, expandTo18Decimals(1))) + }) + }) + } + }) + + describe('#poke', () => { + const tickLower = startingTick - tickSpacing + const tickUpper = startingTick + tickSpacing + + it('best case', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) + await pool.burn(tickLower, tickUpper, 0) + await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) + await snapshotGasCost(pool.burn(tickLower, tickUpper, 0)) + }) + }) + + describe('#collect', () => { + const tickLower = startingTick - tickSpacing + const tickUpper = startingTick + tickSpacing + + it('close to worst case', async () => { + await mint(wallet.address, tickLower, tickUpper, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1).div(100), wallet.address) + await pool.burn(tickLower, tickUpper, 0) // poke to accumulate fees + await snapshotGasCost(pool.collect(wallet.address, tickLower, tickUpper, MaxUint128, MaxUint128)) + }) + }) + + describe('#increaseObservationCardinalityNext', () => { + it('grow by 1 slot', async () => { + await snapshotGasCost(pool.increaseObservationCardinalityNext(5)) + }) + it('no op', async () => { + await snapshotGasCost(pool.increaseObservationCardinalityNext(3)) + }) + }) + + describe('#snapshotCumulativesInside', () => { + it('tick inside', async () => { + await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) + }) + it('tick above', async () => { + await swapToHigherPrice(MAX_SQRT_RATIO.sub(1), wallet.address) + await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) + }) + it('tick below', async () => { + await swapToLowerPrice(MIN_SQRT_RATIO.add(1), wallet.address) + await snapshotGasCost(pool.estimateGas.snapshotCumulativesInside(minTick, maxTick)) + }) + }) + }) + } +}) diff --git a/lib/uniswap/v3-core/test/UniswapV3Pool.spec.ts b/lib/uniswap/v3-core/test/UniswapV3Pool.spec.ts new file mode 100644 index 0000000..dfd70a6 --- /dev/null +++ b/lib/uniswap/v3-core/test/UniswapV3Pool.spec.ts @@ -0,0 +1,1997 @@ +import { ethers, waffle } from 'hardhat' +import { BigNumber, BigNumberish, constants, Wallet } from 'ethers' +import { TestERC20 } from '../typechain/TestERC20' +import { UniswapV3Factory } from '../typechain/UniswapV3Factory' +import { MockTimeUniswapV3Pool } from '../typechain/MockTimeUniswapV3Pool' +import { TestUniswapV3SwapPay } from '../typechain/TestUniswapV3SwapPay' +import checkObservationEquals from './shared/checkObservationEquals' +import { expect } from './shared/expect' + +import { poolFixture, TEST_POOL_START_TIME } from './shared/fixtures' + +import { + expandTo18Decimals, + FeeAmount, + getPositionKey, + getMaxTick, + getMinTick, + encodePriceSqrt, + TICK_SPACINGS, + createPoolFunctions, + SwapFunction, + MintFunction, + getMaxLiquidityPerTick, + FlashFunction, + MaxUint128, + MAX_SQRT_RATIO, + MIN_SQRT_RATIO, + SwapToPriceFunction, +} from './shared/utilities' +import { TestUniswapV3Callee } from '../typechain/TestUniswapV3Callee' +import { TestUniswapV3ReentrantCallee } from '../typechain/TestUniswapV3ReentrantCallee' +import { TickMathTest } from '../typechain/TickMathTest' +import { SwapMathTest } from '../typechain/SwapMathTest' + +const createFixtureLoader = waffle.createFixtureLoader + +type ThenArg = T extends PromiseLike ? U : T + +describe('UniswapV3Pool', () => { + let wallet: Wallet, other: Wallet + + let token0: TestERC20 + let token1: TestERC20 + let token2: TestERC20 + + let factory: UniswapV3Factory + let pool: MockTimeUniswapV3Pool + + let swapTarget: TestUniswapV3Callee + + let swapToLowerPrice: SwapToPriceFunction + let swapToHigherPrice: SwapToPriceFunction + let swapExact0For1: SwapFunction + let swap0ForExact1: SwapFunction + let swapExact1For0: SwapFunction + let swap1ForExact0: SwapFunction + + let feeAmount: number + let tickSpacing: number + + let minTick: number + let maxTick: number + + let mint: MintFunction + let flash: FlashFunction + + let loadFixture: ReturnType + let createPool: ThenArg>['createPool'] + + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + loadFixture = createFixtureLoader([wallet, other]) + }) + + beforeEach('deploy fixture', async () => { + ;({ token0, token1, token2, factory, createPool, swapTargetCallee: swapTarget } = await loadFixture(poolFixture)) + + const oldCreatePool = createPool + createPool = async (_feeAmount, _tickSpacing) => { + const pool = await oldCreatePool(_feeAmount, _tickSpacing) + ;({ + swapToLowerPrice, + swapToHigherPrice, + swapExact0For1, + swap0ForExact1, + swapExact1For0, + swap1ForExact0, + mint, + flash, + } = createPoolFunctions({ + token0, + token1, + swapTarget, + pool, + })) + minTick = getMinTick(_tickSpacing) + maxTick = getMaxTick(_tickSpacing) + feeAmount = _feeAmount + tickSpacing = _tickSpacing + return pool + } + + // default to the 30 bips pool + pool = await createPool(FeeAmount.MEDIUM, TICK_SPACINGS[FeeAmount.MEDIUM]) + }) + + it('constructor initializes immutables', async () => { + expect(await pool.factory()).to.eq(factory.address) + expect(await pool.token0()).to.eq(token0.address) + expect(await pool.token1()).to.eq(token1.address) + expect(await pool.maxLiquidityPerTick()).to.eq(getMaxLiquidityPerTick(tickSpacing)) + }) + + describe('#initialize', () => { + it('fails if already initialized', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await expect(pool.initialize(encodePriceSqrt(1, 1))).to.be.reverted + }) + it('fails if starting price is too low', async () => { + await expect(pool.initialize(1)).to.be.revertedWith('R') + await expect(pool.initialize(MIN_SQRT_RATIO.sub(1))).to.be.revertedWith('R') + }) + it('fails if starting price is too high', async () => { + await expect(pool.initialize(MAX_SQRT_RATIO)).to.be.revertedWith('R') + await expect(pool.initialize(BigNumber.from(2).pow(160).sub(1))).to.be.revertedWith('R') + }) + it('can be initialized at MIN_SQRT_RATIO', async () => { + await pool.initialize(MIN_SQRT_RATIO) + expect((await pool.slot0()).tick).to.eq(getMinTick(1)) + }) + it('can be initialized at MAX_SQRT_RATIO - 1', async () => { + await pool.initialize(MAX_SQRT_RATIO.sub(1)) + expect((await pool.slot0()).tick).to.eq(getMaxTick(1) - 1) + }) + it('sets initial variables', async () => { + const price = encodePriceSqrt(1, 2) + await pool.initialize(price) + + const { sqrtPriceX96, observationIndex } = await pool.slot0() + expect(sqrtPriceX96).to.eq(price) + expect(observationIndex).to.eq(0) + expect((await pool.slot0()).tick).to.eq(-6932) + }) + it('initializes the first observations slot', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + checkObservationEquals(await pool.observations(0), { + secondsPerLiquidityCumulativeX128: 0, + initialized: true, + blockTimestamp: TEST_POOL_START_TIME, + tickCumulative: 0, + }) + }) + it('emits a Initialized event with the input tick', async () => { + const sqrtPriceX96 = encodePriceSqrt(1, 2) + await expect(pool.initialize(sqrtPriceX96)).to.emit(pool, 'Initialize').withArgs(sqrtPriceX96, -6932) + }) + }) + + describe('#increaseObservationCardinalityNext', () => { + it('can only be called after initialize', async () => { + await expect(pool.increaseObservationCardinalityNext(2)).to.be.revertedWith('LOK') + }) + it('emits an event including both old and new', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await expect(pool.increaseObservationCardinalityNext(2)) + .to.emit(pool, 'IncreaseObservationCardinalityNext') + .withArgs(1, 2) + }) + it('does not emit an event for no op call', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await pool.increaseObservationCardinalityNext(3) + await expect(pool.increaseObservationCardinalityNext(2)).to.not.emit(pool, 'IncreaseObservationCardinalityNext') + }) + it('does not change cardinality next if less than current', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await pool.increaseObservationCardinalityNext(3) + await pool.increaseObservationCardinalityNext(2) + expect((await pool.slot0()).observationCardinalityNext).to.eq(3) + }) + it('increases cardinality and cardinality next first time', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await pool.increaseObservationCardinalityNext(2) + const { observationCardinality, observationCardinalityNext } = await pool.slot0() + expect(observationCardinality).to.eq(1) + expect(observationCardinalityNext).to.eq(2) + }) + }) + + describe('#mint', () => { + it('fails if not initialized', async () => { + await expect(mint(wallet.address, -tickSpacing, tickSpacing, 1)).to.be.revertedWith('LOK') + }) + describe('after initialization', () => { + beforeEach('initialize the pool at price of 10:1', async () => { + await pool.initialize(encodePriceSqrt(1, 10)) + await mint(wallet.address, minTick, maxTick, 3161) + }) + + describe('failure cases', () => { + it('fails if tickLower greater than tickUpper', async () => { + // should be TLU but...hardhat + await expect(mint(wallet.address, 1, 0, 1)).to.be.reverted + }) + it('fails if tickLower less than min tick', async () => { + // should be TLM but...hardhat + await expect(mint(wallet.address, -887273, 0, 1)).to.be.reverted + }) + it('fails if tickUpper greater than max tick', async () => { + // should be TUM but...hardhat + await expect(mint(wallet.address, 0, 887273, 1)).to.be.reverted + }) + it('fails if amount exceeds the max', async () => { + // these should fail with 'LO' but hardhat is bugged + const maxLiquidityGross = await pool.maxLiquidityPerTick() + await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross.add(1))).to + .be.reverted + await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross)).to.not.be + .reverted + }) + it('fails if total amount at tick exceeds the max', async () => { + // these should fail with 'LO' but hardhat is bugged + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 1000) + + const maxLiquidityGross = await pool.maxLiquidityPerTick() + await expect( + mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross.sub(1000).add(1)) + ).to.be.reverted + await expect( + mint(wallet.address, minTick + tickSpacing * 2, maxTick - tickSpacing, maxLiquidityGross.sub(1000).add(1)) + ).to.be.reverted + await expect( + mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing * 2, maxLiquidityGross.sub(1000).add(1)) + ).to.be.reverted + await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, maxLiquidityGross.sub(1000))) + .to.not.be.reverted + }) + it('fails if amount is 0', async () => { + await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 0)).to.be.reverted + }) + }) + + describe('success cases', () => { + it('initial balances', async () => { + expect(await token0.balanceOf(pool.address)).to.eq(9996) + expect(await token1.balanceOf(pool.address)).to.eq(1000) + }) + + it('initial tick', async () => { + expect((await pool.slot0()).tick).to.eq(-23028) + }) + + describe('above current price', () => { + it('transfers token0 only', async () => { + await expect(mint(wallet.address, -22980, 0, 10000)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 21549) + .to.not.emit(token1, 'Transfer') + expect(await token0.balanceOf(pool.address)).to.eq(9996 + 21549) + expect(await token1.balanceOf(pool.address)).to.eq(1000) + }) + + it('max tick with max leverage', async () => { + await mint(wallet.address, maxTick - tickSpacing, maxTick, BigNumber.from(2).pow(102)) + expect(await token0.balanceOf(pool.address)).to.eq(9996 + 828011525) + expect(await token1.balanceOf(pool.address)).to.eq(1000) + }) + + it('works for max tick', async () => { + await expect(mint(wallet.address, -22980, maxTick, 10000)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 31549) + expect(await token0.balanceOf(pool.address)).to.eq(9996 + 31549) + expect(await token1.balanceOf(pool.address)).to.eq(1000) + }) + + it('removing works', async () => { + await mint(wallet.address, -240, 0, 10000) + await pool.burn(-240, 0, 10000) + const { amount0, amount1 } = await pool.callStatic.collect(wallet.address, -240, 0, MaxUint128, MaxUint128) + expect(amount0, 'amount0').to.eq(120) + expect(amount1, 'amount1').to.eq(0) + }) + + it('adds liquidity to liquidityGross', async () => { + await mint(wallet.address, -240, 0, 100) + expect((await pool.ticks(-240)).liquidityGross).to.eq(100) + expect((await pool.ticks(0)).liquidityGross).to.eq(100) + expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(0) + expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(0) + await mint(wallet.address, -240, tickSpacing, 150) + expect((await pool.ticks(-240)).liquidityGross).to.eq(250) + expect((await pool.ticks(0)).liquidityGross).to.eq(100) + expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(150) + expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(0) + await mint(wallet.address, 0, tickSpacing * 2, 60) + expect((await pool.ticks(-240)).liquidityGross).to.eq(250) + expect((await pool.ticks(0)).liquidityGross).to.eq(160) + expect((await pool.ticks(tickSpacing)).liquidityGross).to.eq(150) + expect((await pool.ticks(tickSpacing * 2)).liquidityGross).to.eq(60) + }) + + it('removes liquidity from liquidityGross', async () => { + await mint(wallet.address, -240, 0, 100) + await mint(wallet.address, -240, 0, 40) + await pool.burn(-240, 0, 90) + expect((await pool.ticks(-240)).liquidityGross).to.eq(50) + expect((await pool.ticks(0)).liquidityGross).to.eq(50) + }) + + it('clears tick lower if last position is removed', async () => { + await mint(wallet.address, -240, 0, 100) + await pool.burn(-240, 0, 100) + const { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(-240) + expect(liquidityGross).to.eq(0) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + }) + + it('clears tick upper if last position is removed', async () => { + await mint(wallet.address, -240, 0, 100) + await pool.burn(-240, 0, 100) + const { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(0) + expect(liquidityGross).to.eq(0) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + }) + it('only clears the tick that is not used at all', async () => { + await mint(wallet.address, -240, 0, 100) + await mint(wallet.address, -tickSpacing, 0, 250) + await pool.burn(-240, 0, 100) + + let { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(-240) + expect(liquidityGross).to.eq(0) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + ;({ liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128 } = await pool.ticks(-tickSpacing)) + expect(liquidityGross).to.eq(250) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + }) + + it('does not write an observation', async () => { + checkObservationEquals(await pool.observations(0), { + tickCumulative: 0, + blockTimestamp: TEST_POOL_START_TIME, + initialized: true, + secondsPerLiquidityCumulativeX128: 0, + }) + await pool.advanceTime(1) + await mint(wallet.address, -240, 0, 100) + checkObservationEquals(await pool.observations(0), { + tickCumulative: 0, + blockTimestamp: TEST_POOL_START_TIME, + initialized: true, + secondsPerLiquidityCumulativeX128: 0, + }) + }) + }) + + describe('including current price', () => { + it('price within range: transfers current price of both tokens', async () => { + await expect(mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 317) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 32) + expect(await token0.balanceOf(pool.address)).to.eq(9996 + 317) + expect(await token1.balanceOf(pool.address)).to.eq(1000 + 32) + }) + + it('initializes lower tick', async () => { + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100) + const { liquidityGross } = await pool.ticks(minTick + tickSpacing) + expect(liquidityGross).to.eq(100) + }) + + it('initializes upper tick', async () => { + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100) + const { liquidityGross } = await pool.ticks(maxTick - tickSpacing) + expect(liquidityGross).to.eq(100) + }) + + it('works for min/max tick', async () => { + await expect(mint(wallet.address, minTick, maxTick, 10000)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 31623) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 3163) + expect(await token0.balanceOf(pool.address)).to.eq(9996 + 31623) + expect(await token1.balanceOf(pool.address)).to.eq(1000 + 3163) + }) + + it('removing works', async () => { + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 100) + await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 100) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick + tickSpacing, + maxTick - tickSpacing, + MaxUint128, + MaxUint128 + ) + expect(amount0, 'amount0').to.eq(316) + expect(amount1, 'amount1').to.eq(31) + }) + + it('writes an observation', async () => { + checkObservationEquals(await pool.observations(0), { + tickCumulative: 0, + blockTimestamp: TEST_POOL_START_TIME, + initialized: true, + secondsPerLiquidityCumulativeX128: 0, + }) + await pool.advanceTime(1) + await mint(wallet.address, minTick, maxTick, 100) + checkObservationEquals(await pool.observations(0), { + tickCumulative: -23028, + blockTimestamp: TEST_POOL_START_TIME + 1, + initialized: true, + secondsPerLiquidityCumulativeX128: '107650226801941937191829992860413859', + }) + }) + }) + + describe('below current price', () => { + it('transfers token1 only', async () => { + await expect(mint(wallet.address, -46080, -23040, 10000)) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 2162) + .to.not.emit(token0, 'Transfer') + expect(await token0.balanceOf(pool.address)).to.eq(9996) + expect(await token1.balanceOf(pool.address)).to.eq(1000 + 2162) + }) + + it('min tick with max leverage', async () => { + await mint(wallet.address, minTick, minTick + tickSpacing, BigNumber.from(2).pow(102)) + expect(await token0.balanceOf(pool.address)).to.eq(9996) + expect(await token1.balanceOf(pool.address)).to.eq(1000 + 828011520) + }) + + it('works for min tick', async () => { + await expect(mint(wallet.address, minTick, -23040, 10000)) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 3161) + expect(await token0.balanceOf(pool.address)).to.eq(9996) + expect(await token1.balanceOf(pool.address)).to.eq(1000 + 3161) + }) + + it('removing works', async () => { + await mint(wallet.address, -46080, -46020, 10000) + await pool.burn(-46080, -46020, 10000) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + -46080, + -46020, + MaxUint128, + MaxUint128 + ) + expect(amount0, 'amount0').to.eq(0) + expect(amount1, 'amount1').to.eq(3) + }) + + it('does not write an observation', async () => { + checkObservationEquals(await pool.observations(0), { + tickCumulative: 0, + blockTimestamp: TEST_POOL_START_TIME, + initialized: true, + secondsPerLiquidityCumulativeX128: 0, + }) + await pool.advanceTime(1) + await mint(wallet.address, -46080, -23040, 100) + checkObservationEquals(await pool.observations(0), { + tickCumulative: 0, + blockTimestamp: TEST_POOL_START_TIME, + initialized: true, + secondsPerLiquidityCumulativeX128: 0, + }) + }) + }) + }) + + it('protocol fees accumulate as expected during swap', async () => { + await pool.setFeeProtocol(6, 6) + + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address) + await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address) + + let { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() + expect(token0ProtocolFees).to.eq('50000000000000') + expect(token1ProtocolFees).to.eq('5000000000000') + }) + + it('positions are protected before protocol fee is turned on', async () => { + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address) + await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address) + + let { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() + expect(token0ProtocolFees).to.eq(0) + expect(token1ProtocolFees).to.eq(0) + + await pool.setFeeProtocol(6, 6) + ;({ token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees()) + expect(token0ProtocolFees).to.eq(0) + expect(token1ProtocolFees).to.eq(0) + }) + + it('poke is not allowed on uninitialized position', async () => { + await mint(other.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1).div(10), wallet.address) + await swapExact1For0(expandTo18Decimals(1).div(100), wallet.address) + + // missing revert reason due to hardhat + await expect(pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 0)).to.be.reverted + + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, 1) + let { liquidity, feeGrowthInside0LastX128, feeGrowthInside1LastX128, tokensOwed1, tokensOwed0 } = + await pool.positions(getPositionKey(wallet.address, minTick + tickSpacing, maxTick - tickSpacing)) + expect(liquidity).to.eq(1) + expect(feeGrowthInside0LastX128).to.eq('102084710076281216349243831104605583') + expect(feeGrowthInside1LastX128).to.eq('10208471007628121634924383110460558') + expect(tokensOwed0, 'tokens owed 0 before').to.eq(0) + expect(tokensOwed1, 'tokens owed 1 before').to.eq(0) + + await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 1) + ;({ liquidity, feeGrowthInside0LastX128, feeGrowthInside1LastX128, tokensOwed1, tokensOwed0 } = + await pool.positions(getPositionKey(wallet.address, minTick + tickSpacing, maxTick - tickSpacing))) + expect(liquidity).to.eq(0) + expect(feeGrowthInside0LastX128).to.eq('102084710076281216349243831104605583') + expect(feeGrowthInside1LastX128).to.eq('10208471007628121634924383110460558') + expect(tokensOwed0, 'tokens owed 0 after').to.eq(3) + expect(tokensOwed1, 'tokens owed 1 after').to.eq(0) + }) + }) + }) + + describe('#burn', () => { + beforeEach('initialize at zero tick', () => initializeAtZeroTick(pool)) + + async function checkTickIsClear(tick: number) { + const { liquidityGross, feeGrowthOutside0X128, feeGrowthOutside1X128, liquidityNet } = await pool.ticks(tick) + expect(liquidityGross).to.eq(0) + expect(feeGrowthOutside0X128).to.eq(0) + expect(feeGrowthOutside1X128).to.eq(0) + expect(liquidityNet).to.eq(0) + } + + async function checkTickIsNotClear(tick: number) { + const { liquidityGross } = await pool.ticks(tick) + expect(liquidityGross).to.not.eq(0) + } + + it('does not clear the position fee growth snapshot if no more liquidity', async () => { + // some activity that would make the ticks non-zero + await pool.advanceTime(10) + await mint(other.address, minTick, maxTick, expandTo18Decimals(1)) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await swapExact1For0(expandTo18Decimals(1), wallet.address) + await pool.connect(other).burn(minTick, maxTick, expandTo18Decimals(1)) + const { liquidity, tokensOwed0, tokensOwed1, feeGrowthInside0LastX128, feeGrowthInside1LastX128 } = + await pool.positions(getPositionKey(other.address, minTick, maxTick)) + expect(liquidity).to.eq(0) + expect(tokensOwed0).to.not.eq(0) + expect(tokensOwed1).to.not.eq(0) + expect(feeGrowthInside0LastX128).to.eq('340282366920938463463374607431768211') + expect(feeGrowthInside1LastX128).to.eq('340282366920938576890830247744589365') + }) + + it('clears the tick if its the last position using it', async () => { + const tickLower = minTick + tickSpacing + const tickUpper = maxTick - tickSpacing + // some activity that would make the ticks non-zero + await pool.advanceTime(10) + await mint(wallet.address, tickLower, tickUpper, 1) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await pool.burn(tickLower, tickUpper, 1) + await checkTickIsClear(tickLower) + await checkTickIsClear(tickUpper) + }) + + it('clears only the lower tick if upper is still used', async () => { + const tickLower = minTick + tickSpacing + const tickUpper = maxTick - tickSpacing + // some activity that would make the ticks non-zero + await pool.advanceTime(10) + await mint(wallet.address, tickLower, tickUpper, 1) + await mint(wallet.address, tickLower + tickSpacing, tickUpper, 1) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await pool.burn(tickLower, tickUpper, 1) + await checkTickIsClear(tickLower) + await checkTickIsNotClear(tickUpper) + }) + + it('clears only the upper tick if lower is still used', async () => { + const tickLower = minTick + tickSpacing + const tickUpper = maxTick - tickSpacing + // some activity that would make the ticks non-zero + await pool.advanceTime(10) + await mint(wallet.address, tickLower, tickUpper, 1) + await mint(wallet.address, tickLower, tickUpper - tickSpacing, 1) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await pool.burn(tickLower, tickUpper, 1) + await checkTickIsNotClear(tickLower) + await checkTickIsClear(tickUpper) + }) + }) + + // the combined amount of liquidity that the pool is initialized with (including the 1 minimum liquidity that is burned) + const initializeLiquidityAmount = expandTo18Decimals(2) + async function initializeAtZeroTick(pool: MockTimeUniswapV3Pool): Promise { + await pool.initialize(encodePriceSqrt(1, 1)) + const tickSpacing = await pool.tickSpacing() + const [min, max] = [getMinTick(tickSpacing), getMaxTick(tickSpacing)] + await mint(wallet.address, min, max, initializeLiquidityAmount) + } + + describe('#observe', () => { + beforeEach(() => initializeAtZeroTick(pool)) + + // zero tick + it('current tick accumulator increases by tick over time', async () => { + let { + tickCumulatives: [tickCumulative], + } = await pool.observe([0]) + expect(tickCumulative).to.eq(0) + await pool.advanceTime(10) + ;({ + tickCumulatives: [tickCumulative], + } = await pool.observe([0])) + expect(tickCumulative).to.eq(0) + }) + + it('current tick accumulator after single swap', async () => { + // moves to tick -1 + await swapExact0For1(1000, wallet.address) + await pool.advanceTime(4) + let { + tickCumulatives: [tickCumulative], + } = await pool.observe([0]) + expect(tickCumulative).to.eq(-4) + }) + + it('current tick accumulator after two swaps', async () => { + await swapExact0For1(expandTo18Decimals(1).div(2), wallet.address) + expect((await pool.slot0()).tick).to.eq(-4452) + await pool.advanceTime(4) + await swapExact1For0(expandTo18Decimals(1).div(4), wallet.address) + expect((await pool.slot0()).tick).to.eq(-1558) + await pool.advanceTime(6) + let { + tickCumulatives: [tickCumulative], + } = await pool.observe([0]) + // -4452*4 + -1558*6 + expect(tickCumulative).to.eq(-27156) + }) + }) + + describe('miscellaneous mint tests', () => { + beforeEach('initialize at zero tick', async () => { + pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]) + await initializeAtZeroTick(pool) + }) + + it('mint to the right of the current price', async () => { + const liquidityDelta = 1000 + const lowerTick = tickSpacing + const upperTick = tickSpacing * 2 + + const liquidityBefore = await pool.liquidity() + + const b0 = await token0.balanceOf(pool.address) + const b1 = await token1.balanceOf(pool.address) + + await mint(wallet.address, lowerTick, upperTick, liquidityDelta) + + const liquidityAfter = await pool.liquidity() + expect(liquidityAfter).to.be.gte(liquidityBefore) + + expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(1) + expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(0) + }) + + it('mint to the left of the current price', async () => { + const liquidityDelta = 1000 + const lowerTick = -tickSpacing * 2 + const upperTick = -tickSpacing + + const liquidityBefore = await pool.liquidity() + + const b0 = await token0.balanceOf(pool.address) + const b1 = await token1.balanceOf(pool.address) + + await mint(wallet.address, lowerTick, upperTick, liquidityDelta) + + const liquidityAfter = await pool.liquidity() + expect(liquidityAfter).to.be.gte(liquidityBefore) + + expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(0) + expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(1) + }) + + it('mint within the current price', async () => { + const liquidityDelta = 1000 + const lowerTick = -tickSpacing + const upperTick = tickSpacing + + const liquidityBefore = await pool.liquidity() + + const b0 = await token0.balanceOf(pool.address) + const b1 = await token1.balanceOf(pool.address) + + await mint(wallet.address, lowerTick, upperTick, liquidityDelta) + + const liquidityAfter = await pool.liquidity() + expect(liquidityAfter).to.be.gte(liquidityBefore) + + expect((await token0.balanceOf(pool.address)).sub(b0)).to.eq(1) + expect((await token1.balanceOf(pool.address)).sub(b1)).to.eq(1) + }) + + it('cannot remove more than the entire position', async () => { + const lowerTick = -tickSpacing + const upperTick = tickSpacing + await mint(wallet.address, lowerTick, upperTick, expandTo18Decimals(1000)) + // should be 'LS', hardhat is bugged + await expect(pool.burn(lowerTick, upperTick, expandTo18Decimals(1001))).to.be.reverted + }) + + it('collect fees within the current price after swap', async () => { + const liquidityDelta = expandTo18Decimals(100) + const lowerTick = -tickSpacing * 100 + const upperTick = tickSpacing * 100 + + await mint(wallet.address, lowerTick, upperTick, liquidityDelta) + + const liquidityBefore = await pool.liquidity() + + const amount0In = expandTo18Decimals(1) + await swapExact0For1(amount0In, wallet.address) + + const liquidityAfter = await pool.liquidity() + expect(liquidityAfter, 'k increases').to.be.gte(liquidityBefore) + + const token0BalanceBeforePool = await token0.balanceOf(pool.address) + const token1BalanceBeforePool = await token1.balanceOf(pool.address) + const token0BalanceBeforeWallet = await token0.balanceOf(wallet.address) + const token1BalanceBeforeWallet = await token1.balanceOf(wallet.address) + + await pool.burn(lowerTick, upperTick, 0) + await pool.collect(wallet.address, lowerTick, upperTick, MaxUint128, MaxUint128) + + await pool.burn(lowerTick, upperTick, 0) + const { amount0: fees0, amount1: fees1 } = await pool.callStatic.collect( + wallet.address, + lowerTick, + upperTick, + MaxUint128, + MaxUint128 + ) + expect(fees0).to.be.eq(0) + expect(fees1).to.be.eq(0) + + const token0BalanceAfterWallet = await token0.balanceOf(wallet.address) + const token1BalanceAfterWallet = await token1.balanceOf(wallet.address) + const token0BalanceAfterPool = await token0.balanceOf(pool.address) + const token1BalanceAfterPool = await token1.balanceOf(pool.address) + + expect(token0BalanceAfterWallet).to.be.gt(token0BalanceBeforeWallet) + expect(token1BalanceAfterWallet).to.be.eq(token1BalanceBeforeWallet) + + expect(token0BalanceAfterPool).to.be.lt(token0BalanceBeforePool) + expect(token1BalanceAfterPool).to.be.eq(token1BalanceBeforePool) + }) + }) + + describe('post-initialize at medium fee', () => { + describe('k (implicit)', () => { + it('returns 0 before initialization', async () => { + expect(await pool.liquidity()).to.eq(0) + }) + describe('post initialized', () => { + beforeEach(() => initializeAtZeroTick(pool)) + + it('returns initial liquidity', async () => { + expect(await pool.liquidity()).to.eq(expandTo18Decimals(2)) + }) + it('returns in supply in range', async () => { + await mint(wallet.address, -tickSpacing, tickSpacing, expandTo18Decimals(3)) + expect(await pool.liquidity()).to.eq(expandTo18Decimals(5)) + }) + it('excludes supply at tick above current tick', async () => { + await mint(wallet.address, tickSpacing, tickSpacing * 2, expandTo18Decimals(3)) + expect(await pool.liquidity()).to.eq(expandTo18Decimals(2)) + }) + it('excludes supply at tick below current tick', async () => { + await mint(wallet.address, -tickSpacing * 2, -tickSpacing, expandTo18Decimals(3)) + expect(await pool.liquidity()).to.eq(expandTo18Decimals(2)) + }) + it('updates correctly when exiting range', async () => { + const kBefore = await pool.liquidity() + expect(kBefore).to.be.eq(expandTo18Decimals(2)) + + // add liquidity at and above current tick + const liquidityDelta = expandTo18Decimals(1) + const lowerTick = 0 + const upperTick = tickSpacing + await mint(wallet.address, lowerTick, upperTick, liquidityDelta) + + // ensure virtual supply has increased appropriately + const kAfter = await pool.liquidity() + expect(kAfter).to.be.eq(expandTo18Decimals(3)) + + // swap toward the left (just enough for the tick transition function to trigger) + await swapExact0For1(1, wallet.address) + const { tick } = await pool.slot0() + expect(tick).to.be.eq(-1) + + const kAfterSwap = await pool.liquidity() + expect(kAfterSwap).to.be.eq(expandTo18Decimals(2)) + }) + it('updates correctly when entering range', async () => { + const kBefore = await pool.liquidity() + expect(kBefore).to.be.eq(expandTo18Decimals(2)) + + // add liquidity below the current tick + const liquidityDelta = expandTo18Decimals(1) + const lowerTick = -tickSpacing + const upperTick = 0 + await mint(wallet.address, lowerTick, upperTick, liquidityDelta) + + // ensure virtual supply hasn't changed + const kAfter = await pool.liquidity() + expect(kAfter).to.be.eq(kBefore) + + // swap toward the left (just enough for the tick transition function to trigger) + await swapExact0For1(1, wallet.address) + const { tick } = await pool.slot0() + expect(tick).to.be.eq(-1) + + const kAfterSwap = await pool.liquidity() + expect(kAfterSwap).to.be.eq(expandTo18Decimals(3)) + }) + }) + }) + }) + + describe('limit orders', () => { + beforeEach('initialize at tick 0', () => initializeAtZeroTick(pool)) + + it('limit selling 0 for 1 at tick 0 thru 1', async () => { + await expect(mint(wallet.address, 0, 120, expandTo18Decimals(1))) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, '5981737760509663') + // somebody takes the limit order + await swapExact1For0(expandTo18Decimals(2), other.address) + await expect(pool.burn(0, 120, expandTo18Decimals(1))) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, 0, 120, expandTo18Decimals(1), 0, '6017734268818165') + .to.not.emit(token0, 'Transfer') + .to.not.emit(token1, 'Transfer') + await expect(pool.collect(wallet.address, 0, 120, MaxUint128, MaxUint128)) + .to.emit(token1, 'Transfer') + .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('18107525382602')) // roughly 0.3% despite other liquidity + .to.not.emit(token0, 'Transfer') + expect((await pool.slot0()).tick).to.be.gte(120) + }) + it('limit selling 1 for 0 at tick 0 thru -1', async () => { + await expect(mint(wallet.address, -120, 0, expandTo18Decimals(1))) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, '5981737760509663') + // somebody takes the limit order + await swapExact0For1(expandTo18Decimals(2), other.address) + await expect(pool.burn(-120, 0, expandTo18Decimals(1))) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, -120, 0, expandTo18Decimals(1), '6017734268818165', 0) + .to.not.emit(token0, 'Transfer') + .to.not.emit(token1, 'Transfer') + await expect(pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('18107525382602')) // roughly 0.3% despite other liquidity + expect((await pool.slot0()).tick).to.be.lt(-120) + }) + + describe('fee is on', () => { + beforeEach(() => pool.setFeeProtocol(6, 6)) + it('limit selling 0 for 1 at tick 0 thru 1', async () => { + await expect(mint(wallet.address, 0, 120, expandTo18Decimals(1))) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, '5981737760509663') + // somebody takes the limit order + await swapExact1For0(expandTo18Decimals(2), other.address) + await expect(pool.burn(0, 120, expandTo18Decimals(1))) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, 0, 120, expandTo18Decimals(1), 0, '6017734268818165') + .to.not.emit(token0, 'Transfer') + .to.not.emit(token1, 'Transfer') + await expect(pool.collect(wallet.address, 0, 120, MaxUint128, MaxUint128)) + .to.emit(token1, 'Transfer') + .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('15089604485501')) // roughly 0.25% despite other liquidity + .to.not.emit(token0, 'Transfer') + expect((await pool.slot0()).tick).to.be.gte(120) + }) + it('limit selling 1 for 0 at tick 0 thru -1', async () => { + await expect(mint(wallet.address, -120, 0, expandTo18Decimals(1))) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, '5981737760509663') + // somebody takes the limit order + await swapExact0For1(expandTo18Decimals(2), other.address) + await expect(pool.burn(-120, 0, expandTo18Decimals(1))) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, -120, 0, expandTo18Decimals(1), '6017734268818165', 0) + .to.not.emit(token0, 'Transfer') + .to.not.emit(token1, 'Transfer') + await expect(pool.collect(wallet.address, -120, 0, MaxUint128, MaxUint128)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, wallet.address, BigNumber.from('6017734268818165').add('15089604485501')) // roughly 0.25% despite other liquidity + expect((await pool.slot0()).tick).to.be.lt(-120) + }) + }) + }) + + describe('#collect', () => { + beforeEach(async () => { + pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]) + await pool.initialize(encodePriceSqrt(1, 1)) + }) + + it('works with multiple LPs', async () => { + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) + await mint(wallet.address, minTick + tickSpacing, maxTick - tickSpacing, expandTo18Decimals(2)) + + await swapExact0For1(expandTo18Decimals(1), wallet.address) + + // poke positions + await pool.burn(minTick, maxTick, 0) + await pool.burn(minTick + tickSpacing, maxTick - tickSpacing, 0) + + const { tokensOwed0: tokensOwed0Position0 } = await pool.positions( + getPositionKey(wallet.address, minTick, maxTick) + ) + const { tokensOwed0: tokensOwed0Position1 } = await pool.positions( + getPositionKey(wallet.address, minTick + tickSpacing, maxTick - tickSpacing) + ) + + expect(tokensOwed0Position0).to.be.eq('166666666666667') + expect(tokensOwed0Position1).to.be.eq('333333333333334') + }) + + describe('works across large increases', () => { + beforeEach(async () => { + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) + }) + + // type(uint128).max * 2**128 / 1e18 + // https://www.wolframalpha.com/input/?i=%282**128+-+1%29+*+2**128+%2F+1e18 + const magicNumber = BigNumber.from('115792089237316195423570985008687907852929702298719625575994') + + it('works just before the cap binds', async () => { + await pool.setFeeGrowthGlobal0X128(magicNumber) + await pool.burn(minTick, maxTick, 0) + + const { tokensOwed0, tokensOwed1 } = await pool.positions(getPositionKey(wallet.address, minTick, maxTick)) + + expect(tokensOwed0).to.be.eq(MaxUint128.sub(1)) + expect(tokensOwed1).to.be.eq(0) + }) + + it('works just after the cap binds', async () => { + await pool.setFeeGrowthGlobal0X128(magicNumber.add(1)) + await pool.burn(minTick, maxTick, 0) + + const { tokensOwed0, tokensOwed1 } = await pool.positions(getPositionKey(wallet.address, minTick, maxTick)) + + expect(tokensOwed0).to.be.eq(MaxUint128) + expect(tokensOwed1).to.be.eq(0) + }) + + it('works well after the cap binds', async () => { + await pool.setFeeGrowthGlobal0X128(constants.MaxUint256) + await pool.burn(minTick, maxTick, 0) + + const { tokensOwed0, tokensOwed1 } = await pool.positions(getPositionKey(wallet.address, minTick, maxTick)) + + expect(tokensOwed0).to.be.eq(MaxUint128) + expect(tokensOwed1).to.be.eq(0) + }) + }) + + describe('works across overflow boundaries', () => { + beforeEach(async () => { + await pool.setFeeGrowthGlobal0X128(constants.MaxUint256) + await pool.setFeeGrowthGlobal1X128(constants.MaxUint256) + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(10)) + }) + + it('token0', async () => { + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await pool.burn(minTick, maxTick, 0) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + expect(amount0).to.be.eq('499999999999999') + expect(amount1).to.be.eq(0) + }) + it('token1', async () => { + await swapExact1For0(expandTo18Decimals(1), wallet.address) + await pool.burn(minTick, maxTick, 0) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + expect(amount0).to.be.eq(0) + expect(amount1).to.be.eq('499999999999999') + }) + it('token0 and token1', async () => { + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await swapExact1For0(expandTo18Decimals(1), wallet.address) + await pool.burn(minTick, maxTick, 0) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + expect(amount0).to.be.eq('499999999999999') + expect(amount1).to.be.eq('500000000000000') + }) + }) + }) + + describe('#feeProtocol', () => { + const liquidityAmount = expandTo18Decimals(1000) + + beforeEach(async () => { + pool = await createPool(FeeAmount.LOW, TICK_SPACINGS[FeeAmount.LOW]) + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, liquidityAmount) + }) + + it('is initially set to 0', async () => { + expect((await pool.slot0()).feeProtocol).to.eq(0) + }) + + it('can be changed by the owner', async () => { + await pool.setFeeProtocol(6, 6) + expect((await pool.slot0()).feeProtocol).to.eq(102) + }) + + it('cannot be changed out of bounds', async () => { + await expect(pool.setFeeProtocol(3, 3)).to.be.reverted + await expect(pool.setFeeProtocol(11, 11)).to.be.reverted + }) + + it('cannot be changed by addresses that are not owner', async () => { + await expect(pool.connect(other).setFeeProtocol(6, 6)).to.be.reverted + }) + + async function swapAndGetFeesOwed({ + amount, + zeroForOne, + poke, + }: { + amount: BigNumberish + zeroForOne: boolean + poke: boolean + }) { + await (zeroForOne ? swapExact0For1(amount, wallet.address) : swapExact1For0(amount, wallet.address)) + + if (poke) await pool.burn(minTick, maxTick, 0) + + const { amount0: fees0, amount1: fees1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + + expect(fees0, 'fees owed in token0 are greater than 0').to.be.gte(0) + expect(fees1, 'fees owed in token1 are greater than 0').to.be.gte(0) + + return { token0Fees: fees0, token1Fees: fees1 } + } + + it('position owner gets full fees when protocol fee is off', async () => { + const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + + // 6 bips * 1e18 + expect(token0Fees).to.eq('499999999999999') + expect(token1Fees).to.eq(0) + }) + + it('swap fees accumulate as expected (0 for 1)', async () => { + let token0Fees + let token1Fees + ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + })) + expect(token0Fees).to.eq('499999999999999') + expect(token1Fees).to.eq(0) + ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + })) + expect(token0Fees).to.eq('999999999999998') + expect(token1Fees).to.eq(0) + ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + })) + expect(token0Fees).to.eq('1499999999999997') + expect(token1Fees).to.eq(0) + }) + + it('swap fees accumulate as expected (1 for 0)', async () => { + let token0Fees + let token1Fees + ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: false, + poke: true, + })) + expect(token0Fees).to.eq(0) + expect(token1Fees).to.eq('499999999999999') + ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: false, + poke: true, + })) + expect(token0Fees).to.eq(0) + expect(token1Fees).to.eq('999999999999998') + ;({ token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: false, + poke: true, + })) + expect(token0Fees).to.eq(0) + expect(token1Fees).to.eq('1499999999999997') + }) + + it('position owner gets partial fees when protocol fee is on', async () => { + await pool.setFeeProtocol(6, 6) + + const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + + expect(token0Fees).to.be.eq('416666666666666') + expect(token1Fees).to.be.eq(0) + }) + + describe('#collectProtocol', () => { + it('returns 0 if no fees', async () => { + await pool.setFeeProtocol(6, 6) + const { amount0, amount1 } = await pool.callStatic.collectProtocol(wallet.address, MaxUint128, MaxUint128) + expect(amount0).to.be.eq(0) + expect(amount1).to.be.eq(0) + }) + + it('can collect fees', async () => { + await pool.setFeeProtocol(6, 6) + + await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + + await expect(pool.collectProtocol(other.address, MaxUint128, MaxUint128)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, other.address, '83333333333332') + }) + + it('fees collected can differ between token0 and token1', async () => { + await pool.setFeeProtocol(8, 5) + + await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: false, + }) + await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: false, + poke: false, + }) + + await expect(pool.collectProtocol(other.address, MaxUint128, MaxUint128)) + .to.emit(token0, 'Transfer') + // more token0 fees because it's 1/5th the swap fees + .withArgs(pool.address, other.address, '62499999999999') + .to.emit(token1, 'Transfer') + // less token1 fees because it's 1/8th the swap fees + .withArgs(pool.address, other.address, '99999999999998') + }) + }) + + it('fees collected by lp after two swaps should be double one swap', async () => { + await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + + // 6 bips * 2e18 + expect(token0Fees).to.eq('999999999999998') + expect(token1Fees).to.eq(0) + }) + + it('fees collected after two swaps with fee turned on in middle are fees from last swap (not confiscatory)', async () => { + await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: false, + }) + + await pool.setFeeProtocol(6, 6) + + const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + + expect(token0Fees).to.eq('916666666666666') + expect(token1Fees).to.eq(0) + }) + + it('fees collected by lp after two swaps with intermediate withdrawal', async () => { + await pool.setFeeProtocol(6, 6) + + const { token0Fees, token1Fees } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: true, + }) + + expect(token0Fees).to.eq('416666666666666') + expect(token1Fees).to.eq(0) + + // collect the fees + await pool.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128) + + const { token0Fees: token0FeesNext, token1Fees: token1FeesNext } = await swapAndGetFeesOwed({ + amount: expandTo18Decimals(1), + zeroForOne: true, + poke: false, + }) + + expect(token0FeesNext).to.eq(0) + expect(token1FeesNext).to.eq(0) + + let { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() + expect(token0ProtocolFees).to.eq('166666666666666') + expect(token1ProtocolFees).to.eq(0) + + await pool.burn(minTick, maxTick, 0) // poke to update fees + await expect(pool.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, wallet.address, '416666666666666') + ;({ token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees()) + expect(token0ProtocolFees).to.eq('166666666666666') + expect(token1ProtocolFees).to.eq(0) + }) + }) + + describe('#tickSpacing', () => { + describe('tickSpacing = 12', () => { + beforeEach('deploy pool', async () => { + pool = await createPool(FeeAmount.MEDIUM, 12) + }) + describe('post initialize', () => { + beforeEach('initialize pool', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + }) + it('mint can only be called for multiples of 12', async () => { + await expect(mint(wallet.address, -6, 0, 1)).to.be.reverted + await expect(mint(wallet.address, 0, 6, 1)).to.be.reverted + }) + it('mint can be called with multiples of 12', async () => { + await mint(wallet.address, 12, 24, 1) + await mint(wallet.address, -144, -120, 1) + }) + it('swapping across gaps works in 1 for 0 direction', async () => { + const liquidityAmount = expandTo18Decimals(1).div(4) + await mint(wallet.address, 120000, 121200, liquidityAmount) + await swapExact1For0(expandTo18Decimals(1), wallet.address) + await expect(pool.burn(120000, 121200, liquidityAmount)) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, 120000, 121200, liquidityAmount, '30027458295511', '996999999999999999') + .to.not.emit(token0, 'Transfer') + .to.not.emit(token1, 'Transfer') + expect((await pool.slot0()).tick).to.eq(120196) + }) + it('swapping across gaps works in 0 for 1 direction', async () => { + const liquidityAmount = expandTo18Decimals(1).div(4) + await mint(wallet.address, -121200, -120000, liquidityAmount) + await swapExact0For1(expandTo18Decimals(1), wallet.address) + await expect(pool.burn(-121200, -120000, liquidityAmount)) + .to.emit(pool, 'Burn') + .withArgs(wallet.address, -121200, -120000, liquidityAmount, '996999999999999999', '30027458295511') + .to.not.emit(token0, 'Transfer') + .to.not.emit(token1, 'Transfer') + expect((await pool.slot0()).tick).to.eq(-120197) + }) + }) + }) + }) + + // https://github.com/Uniswap/uniswap-v3-core/issues/214 + it('tick transition cannot run twice if zero for one swap ends at fractional price just below tick', async () => { + pool = await createPool(FeeAmount.MEDIUM, 1) + const sqrtTickMath = (await (await ethers.getContractFactory('TickMathTest')).deploy()) as TickMathTest + const swapMath = (await (await ethers.getContractFactory('SwapMathTest')).deploy()) as SwapMathTest + const p0 = (await sqrtTickMath.getSqrtRatioAtTick(-24081)).add(1) + // initialize at a price of ~0.3 token1/token0 + // meaning if you swap in 2 token0, you should end up getting 0 token1 + await pool.initialize(p0) + expect(await pool.liquidity(), 'current pool liquidity is 1').to.eq(0) + expect((await pool.slot0()).tick, 'pool tick is -24081').to.eq(-24081) + + // add a bunch of liquidity around current price + const liquidity = expandTo18Decimals(1000) + await mint(wallet.address, -24082, -24080, liquidity) + expect(await pool.liquidity(), 'current pool liquidity is now liquidity + 1').to.eq(liquidity) + + await mint(wallet.address, -24082, -24081, liquidity) + expect(await pool.liquidity(), 'current pool liquidity is still liquidity + 1').to.eq(liquidity) + + // check the math works out to moving the price down 1, sending no amount out, and having some amount remaining + { + const { feeAmount, amountIn, amountOut, sqrtQ } = await swapMath.computeSwapStep( + p0, + p0.sub(1), + liquidity, + 3, + FeeAmount.MEDIUM + ) + expect(sqrtQ, 'price moves').to.eq(p0.sub(1)) + expect(feeAmount, 'fee amount is 1').to.eq(1) + expect(amountIn, 'amount in is 1').to.eq(1) + expect(amountOut, 'zero amount out').to.eq(0) + } + + // swap 2 amount in, should get 0 amount out + await expect(swapExact0For1(3, wallet.address)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 3) + .to.not.emit(token1, 'Transfer') + + const { tick, sqrtPriceX96 } = await pool.slot0() + + expect(tick, 'pool is at the next tick').to.eq(-24082) + expect(sqrtPriceX96, 'pool price is still on the p0 boundary').to.eq(p0.sub(1)) + expect(await pool.liquidity(), 'pool has run tick transition and liquidity changed').to.eq(liquidity.mul(2)) + }) + + describe('#flash', () => { + it('fails if not initialized', async () => { + await expect(flash(100, 200, other.address)).to.be.reverted + await expect(flash(100, 0, other.address)).to.be.reverted + await expect(flash(0, 200, other.address)).to.be.reverted + }) + it('fails if no liquidity', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await expect(flash(100, 200, other.address)).to.be.revertedWith('L') + await expect(flash(100, 0, other.address)).to.be.revertedWith('L') + await expect(flash(0, 200, other.address)).to.be.revertedWith('L') + }) + describe('after liquidity added', () => { + let balance0: BigNumber + let balance1: BigNumber + beforeEach('add some tokens', async () => { + await initializeAtZeroTick(pool) + ;[balance0, balance1] = await Promise.all([token0.balanceOf(pool.address), token1.balanceOf(pool.address)]) + }) + + describe('fee off', () => { + it('emits an event', async () => { + await expect(flash(1001, 2001, other.address)) + .to.emit(pool, 'Flash') + .withArgs(swapTarget.address, other.address, 1001, 2001, 4, 7) + }) + + it('transfers the amount0 to the recipient', async () => { + await expect(flash(100, 200, other.address)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, other.address, 100) + }) + it('transfers the amount1 to the recipient', async () => { + await expect(flash(100, 200, other.address)) + .to.emit(token1, 'Transfer') + .withArgs(pool.address, other.address, 200) + }) + it('can flash only token0', async () => { + await expect(flash(101, 0, other.address)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, other.address, 101) + .to.not.emit(token1, 'Transfer') + }) + it('can flash only token1', async () => { + await expect(flash(0, 102, other.address)) + .to.emit(token1, 'Transfer') + .withArgs(pool.address, other.address, 102) + .to.not.emit(token0, 'Transfer') + }) + it('can flash entire token balance', async () => { + await expect(flash(balance0, balance1, other.address)) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, other.address, balance0) + .to.emit(token1, 'Transfer') + .withArgs(pool.address, other.address, balance1) + }) + it('no-op if both amounts are 0', async () => { + await expect(flash(0, 0, other.address)).to.not.emit(token0, 'Transfer').to.not.emit(token1, 'Transfer') + }) + it('fails if flash amount is greater than token balance', async () => { + await expect(flash(balance0.add(1), balance1, other.address)).to.be.reverted + await expect(flash(balance0, balance1.add(1), other.address)).to.be.reverted + }) + it('calls the flash callback on the sender with correct fee amounts', async () => { + await expect(flash(1001, 2002, other.address)).to.emit(swapTarget, 'FlashCallback').withArgs(4, 7) + }) + it('increases the fee growth by the expected amount', async () => { + await flash(1001, 2002, other.address) + expect(await pool.feeGrowthGlobal0X128()).to.eq( + BigNumber.from(4).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + expect(await pool.feeGrowthGlobal1X128()).to.eq( + BigNumber.from(7).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + it('fails if original balance not returned in either token', async () => { + await expect(flash(1000, 0, other.address, 999, 0)).to.be.reverted + await expect(flash(0, 1000, other.address, 0, 999)).to.be.reverted + }) + it('fails if underpays either token', async () => { + await expect(flash(1000, 0, other.address, 1002, 0)).to.be.reverted + await expect(flash(0, 1000, other.address, 0, 1002)).to.be.reverted + }) + it('allows donating token0', async () => { + await expect(flash(0, 0, constants.AddressZero, 567, 0)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 567) + .to.not.emit(token1, 'Transfer') + expect(await pool.feeGrowthGlobal0X128()).to.eq( + BigNumber.from(567).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + it('allows donating token1', async () => { + await expect(flash(0, 0, constants.AddressZero, 0, 678)) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 678) + .to.not.emit(token0, 'Transfer') + expect(await pool.feeGrowthGlobal1X128()).to.eq( + BigNumber.from(678).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + it('allows donating token0 and token1 together', async () => { + await expect(flash(0, 0, constants.AddressZero, 789, 1234)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 789) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 1234) + + expect(await pool.feeGrowthGlobal0X128()).to.eq( + BigNumber.from(789).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + expect(await pool.feeGrowthGlobal1X128()).to.eq( + BigNumber.from(1234).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + }) + + describe('fee on', () => { + beforeEach('turn protocol fee on', async () => { + await pool.setFeeProtocol(6, 6) + }) + + it('emits an event', async () => { + await expect(flash(1001, 2001, other.address)) + .to.emit(pool, 'Flash') + .withArgs(swapTarget.address, other.address, 1001, 2001, 4, 7) + }) + + it('increases the fee growth by the expected amount', async () => { + await flash(2002, 4004, other.address) + + const { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() + expect(token0ProtocolFees).to.eq(1) + expect(token1ProtocolFees).to.eq(2) + + expect(await pool.feeGrowthGlobal0X128()).to.eq( + BigNumber.from(6).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + expect(await pool.feeGrowthGlobal1X128()).to.eq( + BigNumber.from(11).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + it('allows donating token0', async () => { + await expect(flash(0, 0, constants.AddressZero, 567, 0)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 567) + .to.not.emit(token1, 'Transfer') + + const { token0: token0ProtocolFees } = await pool.protocolFees() + expect(token0ProtocolFees).to.eq(94) + + expect(await pool.feeGrowthGlobal0X128()).to.eq( + BigNumber.from(473).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + it('allows donating token1', async () => { + await expect(flash(0, 0, constants.AddressZero, 0, 678)) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 678) + .to.not.emit(token0, 'Transfer') + + const { token1: token1ProtocolFees } = await pool.protocolFees() + expect(token1ProtocolFees).to.eq(113) + + expect(await pool.feeGrowthGlobal1X128()).to.eq( + BigNumber.from(565).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + it('allows donating token0 and token1 together', async () => { + await expect(flash(0, 0, constants.AddressZero, 789, 1234)) + .to.emit(token0, 'Transfer') + .withArgs(wallet.address, pool.address, 789) + .to.emit(token1, 'Transfer') + .withArgs(wallet.address, pool.address, 1234) + + const { token0: token0ProtocolFees, token1: token1ProtocolFees } = await pool.protocolFees() + expect(token0ProtocolFees).to.eq(131) + expect(token1ProtocolFees).to.eq(205) + + expect(await pool.feeGrowthGlobal0X128()).to.eq( + BigNumber.from(658).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + expect(await pool.feeGrowthGlobal1X128()).to.eq( + BigNumber.from(1029).mul(BigNumber.from(2).pow(128)).div(expandTo18Decimals(2)) + ) + }) + }) + }) + }) + + describe('#increaseObservationCardinalityNext', () => { + it('cannot be called before initialization', async () => { + await expect(pool.increaseObservationCardinalityNext(2)).to.be.reverted + }) + describe('after initialization', () => { + beforeEach('initialize the pool', () => pool.initialize(encodePriceSqrt(1, 1))) + it('oracle starting state after initialization', async () => { + const { observationCardinality, observationIndex, observationCardinalityNext } = await pool.slot0() + expect(observationCardinality).to.eq(1) + expect(observationIndex).to.eq(0) + expect(observationCardinalityNext).to.eq(1) + const { secondsPerLiquidityCumulativeX128, tickCumulative, initialized, blockTimestamp } = + await pool.observations(0) + expect(secondsPerLiquidityCumulativeX128).to.eq(0) + expect(tickCumulative).to.eq(0) + expect(initialized).to.eq(true) + expect(blockTimestamp).to.eq(TEST_POOL_START_TIME) + }) + it('increases observation cardinality next', async () => { + await pool.increaseObservationCardinalityNext(2) + const { observationCardinality, observationIndex, observationCardinalityNext } = await pool.slot0() + expect(observationCardinality).to.eq(1) + expect(observationIndex).to.eq(0) + expect(observationCardinalityNext).to.eq(2) + }) + it('is no op if target is already exceeded', async () => { + await pool.increaseObservationCardinalityNext(5) + await pool.increaseObservationCardinalityNext(3) + const { observationCardinality, observationIndex, observationCardinalityNext } = await pool.slot0() + expect(observationCardinality).to.eq(1) + expect(observationIndex).to.eq(0) + expect(observationCardinalityNext).to.eq(5) + }) + }) + }) + + describe('#setFeeProtocol', () => { + beforeEach('initialize the pool', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + }) + + it('can only be called by factory owner', async () => { + await expect(pool.connect(other).setFeeProtocol(5, 5)).to.be.reverted + }) + it('fails if fee is lt 4 or gt 10', async () => { + await expect(pool.setFeeProtocol(3, 3)).to.be.reverted + await expect(pool.setFeeProtocol(6, 3)).to.be.reverted + await expect(pool.setFeeProtocol(3, 6)).to.be.reverted + await expect(pool.setFeeProtocol(11, 11)).to.be.reverted + await expect(pool.setFeeProtocol(6, 11)).to.be.reverted + await expect(pool.setFeeProtocol(11, 6)).to.be.reverted + }) + it('succeeds for fee of 4', async () => { + await pool.setFeeProtocol(4, 4) + }) + it('succeeds for fee of 10', async () => { + await pool.setFeeProtocol(10, 10) + }) + it('sets protocol fee', async () => { + await pool.setFeeProtocol(7, 7) + expect((await pool.slot0()).feeProtocol).to.eq(119) + }) + it('can change protocol fee', async () => { + await pool.setFeeProtocol(7, 7) + await pool.setFeeProtocol(5, 8) + expect((await pool.slot0()).feeProtocol).to.eq(133) + }) + it('can turn off protocol fee', async () => { + await pool.setFeeProtocol(4, 4) + await pool.setFeeProtocol(0, 0) + expect((await pool.slot0()).feeProtocol).to.eq(0) + }) + it('emits an event when turned on', async () => { + await expect(pool.setFeeProtocol(7, 7)).to.be.emit(pool, 'SetFeeProtocol').withArgs(0, 0, 7, 7) + }) + it('emits an event when turned off', async () => { + await pool.setFeeProtocol(7, 5) + await expect(pool.setFeeProtocol(0, 0)).to.be.emit(pool, 'SetFeeProtocol').withArgs(7, 5, 0, 0) + }) + it('emits an event when changed', async () => { + await pool.setFeeProtocol(4, 10) + await expect(pool.setFeeProtocol(6, 8)).to.be.emit(pool, 'SetFeeProtocol').withArgs(4, 10, 6, 8) + }) + it('emits an event when unchanged', async () => { + await pool.setFeeProtocol(5, 9) + await expect(pool.setFeeProtocol(5, 9)).to.be.emit(pool, 'SetFeeProtocol').withArgs(5, 9, 5, 9) + }) + }) + + describe('#lock', () => { + beforeEach('initialize the pool', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) + }) + + it('cannot reenter from swap callback', async () => { + const reentrant = (await ( + await ethers.getContractFactory('TestUniswapV3ReentrantCallee') + ).deploy()) as TestUniswapV3ReentrantCallee + + // the tests happen in solidity + await expect(reentrant.swapToReenter(pool.address)).to.be.revertedWith('Unable to reenter') + }) + }) + + describe('#snapshotCumulativesInside', () => { + const tickLower = -TICK_SPACINGS[FeeAmount.MEDIUM] + const tickUpper = TICK_SPACINGS[FeeAmount.MEDIUM] + const tickSpacing = TICK_SPACINGS[FeeAmount.MEDIUM] + beforeEach(async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, tickLower, tickUpper, 10) + }) + it('throws if ticks are in reverse order', async () => { + await expect(pool.snapshotCumulativesInside(tickUpper, tickLower)).to.be.reverted + }) + it('throws if ticks are the same', async () => { + await expect(pool.snapshotCumulativesInside(tickUpper, tickUpper)).to.be.reverted + }) + it('throws if tick lower is too low', async () => { + await expect(pool.snapshotCumulativesInside(getMinTick(tickSpacing) - 1, tickUpper)).be.reverted + }) + it('throws if tick upper is too high', async () => { + await expect(pool.snapshotCumulativesInside(tickLower, getMaxTick(tickSpacing) + 1)).be.reverted + }) + it('throws if tick lower is not initialized', async () => { + await expect(pool.snapshotCumulativesInside(tickLower - tickSpacing, tickUpper)).to.be.reverted + }) + it('throws if tick upper is not initialized', async () => { + await expect(pool.snapshotCumulativesInside(tickLower, tickUpper + tickSpacing)).to.be.reverted + }) + it('is zero immediately after initialize', async () => { + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(0) + expect(tickCumulativeInside).to.eq(0) + expect(secondsInside).to.eq(0) + }) + it('increases by expected amount when time elapses in the range', async () => { + await pool.advanceTime(5) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(10)) + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) + expect(secondsInside).to.eq(5) + }) + it('does not account for time increase above range', async () => { + await pool.advanceTime(5) + await swapToHigherPrice(encodePriceSqrt(2, 1), wallet.address) + await pool.advanceTime(7) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(10)) + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) + expect(secondsInside).to.eq(5) + }) + it('does not account for time increase below range', async () => { + await pool.advanceTime(5) + await swapToLowerPrice(encodePriceSqrt(1, 2), wallet.address) + await pool.advanceTime(7) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(10)) + // tick is 0 for 5 seconds, then not in range + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) + expect(secondsInside).to.eq(5) + }) + it('time increase below range is not counted', async () => { + await swapToLowerPrice(encodePriceSqrt(1, 2), wallet.address) + await pool.advanceTime(5) + await swapToHigherPrice(encodePriceSqrt(1, 1), wallet.address) + await pool.advanceTime(7) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(7).shl(128).div(10)) + // tick is not in range then tick is 0 for 7 seconds + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) + expect(secondsInside).to.eq(7) + }) + it('time increase above range is not counted', async () => { + await swapToHigherPrice(encodePriceSqrt(2, 1), wallet.address) + await pool.advanceTime(5) + await swapToLowerPrice(encodePriceSqrt(1, 1), wallet.address) + await pool.advanceTime(7) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(7).shl(128).div(10)) + expect((await pool.slot0()).tick).to.eq(-1) // justify the -7 tick cumulative inside value + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(-7) + expect(secondsInside).to.eq(7) + }) + it('positions minted after time spent', async () => { + await pool.advanceTime(5) + await mint(wallet.address, tickUpper, getMaxTick(tickSpacing), 15) + await swapToHigherPrice(encodePriceSqrt(2, 1), wallet.address) + await pool.advanceTime(8) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickUpper, getMaxTick(tickSpacing)) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(8).shl(128).div(15)) + // the tick of 2/1 is 6931 + // 8 seconds * 6931 = 55448 + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(55448) + expect(secondsInside).to.eq(8) + }) + it('overlapping liquidity is aggregated', async () => { + await mint(wallet.address, tickLower, getMaxTick(tickSpacing), 15) + await pool.advanceTime(5) + await swapToHigherPrice(encodePriceSqrt(2, 1), wallet.address) + await pool.advanceTime(8) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(tickLower, tickUpper) + expect(secondsPerLiquidityInsideX128).to.eq(BigNumber.from(5).shl(128).div(25)) + expect(tickCumulativeInside, 'tickCumulativeInside').to.eq(0) + expect(secondsInside).to.eq(5) + }) + it('relative behavior of snapshots', async () => { + await pool.advanceTime(5) + await mint(wallet.address, getMinTick(tickSpacing), tickLower, 15) + const { + secondsPerLiquidityInsideX128: secondsPerLiquidityInsideX128Start, + tickCumulativeInside: tickCumulativeInsideStart, + secondsInside: secondsInsideStart, + } = await pool.snapshotCumulativesInside(getMinTick(tickSpacing), tickLower) + await pool.advanceTime(8) + // 13 seconds in starting range, then 3 seconds in newly minted range + await swapToLowerPrice(encodePriceSqrt(1, 2), wallet.address) + await pool.advanceTime(3) + const { secondsPerLiquidityInsideX128, tickCumulativeInside, secondsInside } = + await pool.snapshotCumulativesInside(getMinTick(tickSpacing), tickLower) + const expectedDiffSecondsPerLiquidity = BigNumber.from(3).shl(128).div(15) + expect(secondsPerLiquidityInsideX128.sub(secondsPerLiquidityInsideX128Start)).to.eq( + expectedDiffSecondsPerLiquidity + ) + expect(secondsPerLiquidityInsideX128).to.not.eq(expectedDiffSecondsPerLiquidity) + // the tick is the one corresponding to the price of 1/2, or log base 1.0001 of 0.5 + // this is -6932, and 3 seconds have passed, so the cumulative computed from the diff equals 6932 * 3 + expect(tickCumulativeInside.sub(tickCumulativeInsideStart), 'tickCumulativeInside').to.eq(-20796) + expect(secondsInside - secondsInsideStart).to.eq(3) + expect(secondsInside).to.not.eq(3) + }) + }) + + describe('fees overflow scenarios', async () => { + it('up to max uint 128', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, 1) + await flash(0, 0, wallet.address, MaxUint128, MaxUint128) + + const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([ + pool.feeGrowthGlobal0X128(), + pool.feeGrowthGlobal1X128(), + ]) + // all 1s in first 128 bits + expect(feeGrowthGlobal0X128).to.eq(MaxUint128.shl(128)) + expect(feeGrowthGlobal1X128).to.eq(MaxUint128.shl(128)) + await pool.burn(minTick, maxTick, 0) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + expect(amount0).to.eq(MaxUint128) + expect(amount1).to.eq(MaxUint128) + }) + + it('overflow max uint 128', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, 1) + await flash(0, 0, wallet.address, MaxUint128, MaxUint128) + await flash(0, 0, wallet.address, 1, 1) + + const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([ + pool.feeGrowthGlobal0X128(), + pool.feeGrowthGlobal1X128(), + ]) + // all 1s in first 128 bits + expect(feeGrowthGlobal0X128).to.eq(0) + expect(feeGrowthGlobal1X128).to.eq(0) + await pool.burn(minTick, maxTick, 0) + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + // fees burned + expect(amount0).to.eq(0) + expect(amount1).to.eq(0) + }) + + it('overflow max uint 128 after poke burns fees owed to 0', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, 1) + await flash(0, 0, wallet.address, MaxUint128, MaxUint128) + await pool.burn(minTick, maxTick, 0) + await flash(0, 0, wallet.address, 1, 1) + await pool.burn(minTick, maxTick, 0) + + const { amount0, amount1 } = await pool.callStatic.collect( + wallet.address, + minTick, + maxTick, + MaxUint128, + MaxUint128 + ) + // fees burned + expect(amount0).to.eq(0) + expect(amount1).to.eq(0) + }) + + it('two positions at the same snapshot', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, 1) + await mint(other.address, minTick, maxTick, 1) + await flash(0, 0, wallet.address, MaxUint128, 0) + await flash(0, 0, wallet.address, MaxUint128, 0) + const feeGrowthGlobal0X128 = await pool.feeGrowthGlobal0X128() + expect(feeGrowthGlobal0X128).to.eq(MaxUint128.shl(128)) + await flash(0, 0, wallet.address, 2, 0) + await pool.burn(minTick, maxTick, 0) + await pool.connect(other).burn(minTick, maxTick, 0) + let { amount0 } = await pool.callStatic.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128) + expect(amount0, 'amount0 of wallet').to.eq(0) + ;({ amount0 } = await pool + .connect(other) + .callStatic.collect(other.address, minTick, maxTick, MaxUint128, MaxUint128)) + expect(amount0, 'amount0 of other').to.eq(0) + }) + + it('two positions 1 wei of fees apart overflows exactly once', async () => { + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, 1) + await flash(0, 0, wallet.address, 1, 0) + await mint(other.address, minTick, maxTick, 1) + await flash(0, 0, wallet.address, MaxUint128, 0) + await flash(0, 0, wallet.address, MaxUint128, 0) + const feeGrowthGlobal0X128 = await pool.feeGrowthGlobal0X128() + expect(feeGrowthGlobal0X128).to.eq(0) + await flash(0, 0, wallet.address, 2, 0) + await pool.burn(minTick, maxTick, 0) + await pool.connect(other).burn(minTick, maxTick, 0) + let { amount0 } = await pool.callStatic.collect(wallet.address, minTick, maxTick, MaxUint128, MaxUint128) + expect(amount0, 'amount0 of wallet').to.eq(1) + ;({ amount0 } = await pool + .connect(other) + .callStatic.collect(other.address, minTick, maxTick, MaxUint128, MaxUint128)) + expect(amount0, 'amount0 of other').to.eq(0) + }) + }) + + describe('swap underpayment tests', () => { + let underpay: TestUniswapV3SwapPay + beforeEach('deploy swap test', async () => { + const underpayFactory = await ethers.getContractFactory('TestUniswapV3SwapPay') + underpay = (await underpayFactory.deploy()) as TestUniswapV3SwapPay + await token0.approve(underpay.address, constants.MaxUint256) + await token1.approve(underpay.address, constants.MaxUint256) + await pool.initialize(encodePriceSqrt(1, 1)) + await mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) + }) + + it('underpay zero for one and exact in', async () => { + await expect( + underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), 1000, 1, 0) + ).to.be.revertedWith('IIA') + }) + it('pay in the wrong token zero for one and exact in', async () => { + await expect( + underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), 1000, 0, 2000) + ).to.be.revertedWith('IIA') + }) + it('overpay zero for one and exact in', async () => { + await expect( + underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), 1000, 2000, 0) + ).to.not.be.revertedWith('IIA') + }) + it('underpay zero for one and exact out', async () => { + await expect( + underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), -1000, 1, 0) + ).to.be.revertedWith('IIA') + }) + it('pay in the wrong token zero for one and exact out', async () => { + await expect( + underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), -1000, 0, 2000) + ).to.be.revertedWith('IIA') + }) + it('overpay zero for one and exact out', async () => { + await expect( + underpay.swap(pool.address, wallet.address, true, MIN_SQRT_RATIO.add(1), -1000, 2000, 0) + ).to.not.be.revertedWith('IIA') + }) + it('underpay one for zero and exact in', async () => { + await expect( + underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), 1000, 0, 1) + ).to.be.revertedWith('IIA') + }) + it('pay in the wrong token one for zero and exact in', async () => { + await expect( + underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), 1000, 2000, 0) + ).to.be.revertedWith('IIA') + }) + it('overpay one for zero and exact in', async () => { + await expect( + underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), 1000, 0, 2000) + ).to.not.be.revertedWith('IIA') + }) + it('underpay one for zero and exact out', async () => { + await expect( + underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), -1000, 0, 1) + ).to.be.revertedWith('IIA') + }) + it('pay in the wrong token one for zero and exact out', async () => { + await expect( + underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), -1000, 2000, 0) + ).to.be.revertedWith('IIA') + }) + it('overpay one for zero and exact out', async () => { + await expect( + underpay.swap(pool.address, wallet.address, false, MAX_SQRT_RATIO.sub(1), -1000, 0, 2000) + ).to.not.be.revertedWith('IIA') + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/UniswapV3Pool.swaps.spec.ts b/lib/uniswap/v3-core/test/UniswapV3Pool.swaps.spec.ts new file mode 100644 index 0000000..03c76dd --- /dev/null +++ b/lib/uniswap/v3-core/test/UniswapV3Pool.swaps.spec.ts @@ -0,0 +1,590 @@ +import { Decimal } from 'decimal.js' +import { BigNumber, BigNumberish, ContractTransaction, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { MockTimeUniswapV3Pool } from '../typechain/MockTimeUniswapV3Pool' +import { TestERC20 } from '../typechain/TestERC20' + +import { TestUniswapV3Callee } from '../typechain/TestUniswapV3Callee' +import { expect } from './shared/expect' +import { poolFixture } from './shared/fixtures' +import { formatPrice, formatTokenAmount } from './shared/format' +import { + createPoolFunctions, + encodePriceSqrt, + expandTo18Decimals, + FeeAmount, + getMaxLiquidityPerTick, + getMaxTick, + getMinTick, + MAX_SQRT_RATIO, + MaxUint128, + MIN_SQRT_RATIO, + TICK_SPACINGS, +} from './shared/utilities' + +Decimal.config({ toExpNeg: -500, toExpPos: 500 }) + +const createFixtureLoader = waffle.createFixtureLoader +const { constants } = ethers + +interface BaseSwapTestCase { + zeroForOne: boolean + sqrtPriceLimit?: BigNumber +} +interface SwapExact0For1TestCase extends BaseSwapTestCase { + zeroForOne: true + exactOut: false + amount0: BigNumberish + sqrtPriceLimit?: BigNumber +} +interface SwapExact1For0TestCase extends BaseSwapTestCase { + zeroForOne: false + exactOut: false + amount1: BigNumberish + sqrtPriceLimit?: BigNumber +} +interface Swap0ForExact1TestCase extends BaseSwapTestCase { + zeroForOne: true + exactOut: true + amount1: BigNumberish + sqrtPriceLimit?: BigNumber +} +interface Swap1ForExact0TestCase extends BaseSwapTestCase { + zeroForOne: false + exactOut: true + amount0: BigNumberish + sqrtPriceLimit?: BigNumber +} +interface SwapToHigherPrice extends BaseSwapTestCase { + zeroForOne: false + sqrtPriceLimit: BigNumber +} +interface SwapToLowerPrice extends BaseSwapTestCase { + zeroForOne: true + sqrtPriceLimit: BigNumber +} +type SwapTestCase = + | SwapExact0For1TestCase + | Swap0ForExact1TestCase + | SwapExact1For0TestCase + | Swap1ForExact0TestCase + | SwapToHigherPrice + | SwapToLowerPrice + +function swapCaseToDescription(testCase: SwapTestCase): string { + const priceClause = testCase?.sqrtPriceLimit ? ` to price ${formatPrice(testCase.sqrtPriceLimit)}` : '' + if ('exactOut' in testCase) { + if (testCase.exactOut) { + if (testCase.zeroForOne) { + return `swap token0 for exactly ${formatTokenAmount(testCase.amount1)} token1${priceClause}` + } else { + return `swap token1 for exactly ${formatTokenAmount(testCase.amount0)} token0${priceClause}` + } + } else { + if (testCase.zeroForOne) { + return `swap exactly ${formatTokenAmount(testCase.amount0)} token0 for token1${priceClause}` + } else { + return `swap exactly ${formatTokenAmount(testCase.amount1)} token1 for token0${priceClause}` + } + } + } else { + if (testCase.zeroForOne) { + return `swap token0 for token1${priceClause}` + } else { + return `swap token1 for token0${priceClause}` + } + } +} + +type PoolFunctions = ReturnType + +// can't use address zero because the ERC20 token does not allow it +const SWAP_RECIPIENT_ADDRESS = constants.AddressZero.slice(0, -1) + '1' +const POSITION_PROCEEDS_OUTPUT_ADDRESS = constants.AddressZero.slice(0, -1) + '2' + +async function executeSwap( + pool: MockTimeUniswapV3Pool, + testCase: SwapTestCase, + poolFunctions: PoolFunctions +): Promise { + let swap: ContractTransaction + if ('exactOut' in testCase) { + if (testCase.exactOut) { + if (testCase.zeroForOne) { + swap = await poolFunctions.swap0ForExact1(testCase.amount1, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) + } else { + swap = await poolFunctions.swap1ForExact0(testCase.amount0, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) + } + } else { + if (testCase.zeroForOne) { + swap = await poolFunctions.swapExact0For1(testCase.amount0, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) + } else { + swap = await poolFunctions.swapExact1For0(testCase.amount1, SWAP_RECIPIENT_ADDRESS, testCase.sqrtPriceLimit) + } + } + } else { + if (testCase.zeroForOne) { + swap = await poolFunctions.swapToLowerPrice(testCase.sqrtPriceLimit, SWAP_RECIPIENT_ADDRESS) + } else { + swap = await poolFunctions.swapToHigherPrice(testCase.sqrtPriceLimit, SWAP_RECIPIENT_ADDRESS) + } + } + return swap +} + +const DEFAULT_POOL_SWAP_TESTS: SwapTestCase[] = [ + // swap large amounts in/out + { + zeroForOne: true, + exactOut: false, + amount0: expandTo18Decimals(1), + }, + { + zeroForOne: false, + exactOut: false, + amount1: expandTo18Decimals(1), + }, + { + zeroForOne: true, + exactOut: true, + amount1: expandTo18Decimals(1), + }, + { + zeroForOne: false, + exactOut: true, + amount0: expandTo18Decimals(1), + }, + // swap large amounts in/out with a price limit + { + zeroForOne: true, + exactOut: false, + amount0: expandTo18Decimals(1), + sqrtPriceLimit: encodePriceSqrt(50, 100), + }, + { + zeroForOne: false, + exactOut: false, + amount1: expandTo18Decimals(1), + sqrtPriceLimit: encodePriceSqrt(200, 100), + }, + { + zeroForOne: true, + exactOut: true, + amount1: expandTo18Decimals(1), + sqrtPriceLimit: encodePriceSqrt(50, 100), + }, + { + zeroForOne: false, + exactOut: true, + amount0: expandTo18Decimals(1), + sqrtPriceLimit: encodePriceSqrt(200, 100), + }, + // swap small amounts in/out + { + zeroForOne: true, + exactOut: false, + amount0: 1000, + }, + { + zeroForOne: false, + exactOut: false, + amount1: 1000, + }, + { + zeroForOne: true, + exactOut: true, + amount1: 1000, + }, + { + zeroForOne: false, + exactOut: true, + amount0: 1000, + }, + // swap arbitrary input to price + { + sqrtPriceLimit: encodePriceSqrt(5, 2), + zeroForOne: false, + }, + { + sqrtPriceLimit: encodePriceSqrt(2, 5), + zeroForOne: true, + }, + { + sqrtPriceLimit: encodePriceSqrt(5, 2), + zeroForOne: true, + }, + { + sqrtPriceLimit: encodePriceSqrt(2, 5), + zeroForOne: false, + }, +] + +interface Position { + tickLower: number + tickUpper: number + liquidity: BigNumberish +} + +interface PoolTestCase { + description: string + feeAmount: number + tickSpacing: number + startingPrice: BigNumber + positions: Position[] + swapTests?: SwapTestCase[] +} + +const TEST_POOLS: PoolTestCase[] = [ + { + description: 'low fee, 1:1 price, 2e18 max range liquidity', + feeAmount: FeeAmount.LOW, + tickSpacing: TICK_SPACINGS[FeeAmount.LOW], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.LOW]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.LOW]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, 1:1 price, 2e18 max range liquidity', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'high fee, 1:1 price, 2e18 max range liquidity', + feeAmount: FeeAmount.HIGH, + tickSpacing: TICK_SPACINGS[FeeAmount.HIGH], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.HIGH]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.HIGH]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, 10:1 price, 2e18 max range liquidity', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(10, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, 1:10 price, 2e18 max range liquidity', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 10), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, 1:1 price, 0 liquidity, all liquidity around current price', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: -TICK_SPACINGS[FeeAmount.MEDIUM], + liquidity: expandTo18Decimals(2), + }, + { + tickLower: TICK_SPACINGS[FeeAmount.MEDIUM], + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, 1:1 price, additional liquidity around current price', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: -TICK_SPACINGS[FeeAmount.MEDIUM], + liquidity: expandTo18Decimals(2), + }, + { + tickLower: TICK_SPACINGS[FeeAmount.MEDIUM], + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'low fee, large liquidity around current price (stable swap)', + feeAmount: FeeAmount.LOW, + tickSpacing: TICK_SPACINGS[FeeAmount.LOW], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: -TICK_SPACINGS[FeeAmount.LOW], + tickUpper: TICK_SPACINGS[FeeAmount.LOW], + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, token0 liquidity only', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: 0, + tickUpper: 2000 * TICK_SPACINGS[FeeAmount.MEDIUM], + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'medium fee, token1 liquidity only', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: -2000 * TICK_SPACINGS[FeeAmount.MEDIUM], + tickUpper: 0, + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'close to max price', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(BigNumber.from(2).pow(127), 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'close to min price', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, BigNumber.from(2).pow(127)), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'max full range liquidity at 1:1 price with default fee', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: encodePriceSqrt(1, 1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: getMaxLiquidityPerTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + }, + ], + }, + { + description: 'initialized at the max ratio', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: MAX_SQRT_RATIO.sub(1), + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, + { + description: 'initialized at the min ratio', + feeAmount: FeeAmount.MEDIUM, + tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], + startingPrice: MIN_SQRT_RATIO, + positions: [ + { + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + liquidity: expandTo18Decimals(2), + }, + ], + }, +] + +describe('UniswapV3Pool swap tests', () => { + let wallet: Wallet, other: Wallet + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + + loadFixture = createFixtureLoader([wallet]) + }) + + for (const poolCase of TEST_POOLS) { + describe(poolCase.description, () => { + const poolCaseFixture = async () => { + const { + createPool, + token0, + token1, + swapTargetCallee: swapTarget, + } = await poolFixture([wallet], waffle.provider) + const pool = await createPool(poolCase.feeAmount, poolCase.tickSpacing) + const poolFunctions = createPoolFunctions({ swapTarget, token0, token1, pool }) + await pool.initialize(poolCase.startingPrice) + // mint all positions + for (const position of poolCase.positions) { + await poolFunctions.mint(wallet.address, position.tickLower, position.tickUpper, position.liquidity) + } + + const [poolBalance0, poolBalance1] = await Promise.all([ + token0.balanceOf(pool.address), + token1.balanceOf(pool.address), + ]) + + return { token0, token1, pool, poolFunctions, poolBalance0, poolBalance1, swapTarget } + } + + let token0: TestERC20 + let token1: TestERC20 + + let poolBalance0: BigNumber + let poolBalance1: BigNumber + + let pool: MockTimeUniswapV3Pool + let swapTarget: TestUniswapV3Callee + let poolFunctions: PoolFunctions + + beforeEach('load fixture', async () => { + ;({ token0, token1, pool, poolFunctions, poolBalance0, poolBalance1, swapTarget } = await loadFixture( + poolCaseFixture + )) + }) + + afterEach('check can burn positions', async () => { + for (const { liquidity, tickUpper, tickLower } of poolCase.positions) { + await pool.burn(tickLower, tickUpper, liquidity) + await pool.collect(POSITION_PROCEEDS_OUTPUT_ADDRESS, tickLower, tickUpper, MaxUint128, MaxUint128) + } + }) + + for (const testCase of poolCase.swapTests ?? DEFAULT_POOL_SWAP_TESTS) { + it(swapCaseToDescription(testCase), async () => { + const slot0 = await pool.slot0() + const tx = executeSwap(pool, testCase, poolFunctions) + try { + await tx + } catch (error) { + expect({ + swapError: error.message, + poolBalance0: poolBalance0.toString(), + poolBalance1: poolBalance1.toString(), + poolPriceBefore: formatPrice(slot0.sqrtPriceX96), + tickBefore: slot0.tick, + }).to.matchSnapshot('swap error') + return + } + const [ + poolBalance0After, + poolBalance1After, + slot0After, + liquidityAfter, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, + ] = await Promise.all([ + token0.balanceOf(pool.address), + token1.balanceOf(pool.address), + pool.slot0(), + pool.liquidity(), + pool.feeGrowthGlobal0X128(), + pool.feeGrowthGlobal1X128(), + ]) + const poolBalance0Delta = poolBalance0After.sub(poolBalance0) + const poolBalance1Delta = poolBalance1After.sub(poolBalance1) + + // check all the events were emitted corresponding to balance changes + if (poolBalance0Delta.eq(0)) await expect(tx).to.not.emit(token0, 'Transfer') + else if (poolBalance0Delta.lt(0)) + await expect(tx) + .to.emit(token0, 'Transfer') + .withArgs(pool.address, SWAP_RECIPIENT_ADDRESS, poolBalance0Delta.mul(-1)) + else await expect(tx).to.emit(token0, 'Transfer').withArgs(wallet.address, pool.address, poolBalance0Delta) + + if (poolBalance1Delta.eq(0)) await expect(tx).to.not.emit(token1, 'Transfer') + else if (poolBalance1Delta.lt(0)) + await expect(tx) + .to.emit(token1, 'Transfer') + .withArgs(pool.address, SWAP_RECIPIENT_ADDRESS, poolBalance1Delta.mul(-1)) + else await expect(tx).to.emit(token1, 'Transfer').withArgs(wallet.address, pool.address, poolBalance1Delta) + + // check that the swap event was emitted too + await expect(tx) + .to.emit(pool, 'Swap') + .withArgs( + swapTarget.address, + SWAP_RECIPIENT_ADDRESS, + poolBalance0Delta, + poolBalance1Delta, + slot0After.sqrtPriceX96, + liquidityAfter, + slot0After.tick + ) + + const executionPrice = new Decimal(poolBalance1Delta.toString()).div(poolBalance0Delta.toString()).mul(-1) + + expect({ + amount0Before: poolBalance0.toString(), + amount1Before: poolBalance1.toString(), + amount0Delta: poolBalance0Delta.toString(), + amount1Delta: poolBalance1Delta.toString(), + feeGrowthGlobal0X128Delta: feeGrowthGlobal0X128.toString(), + feeGrowthGlobal1X128Delta: feeGrowthGlobal1X128.toString(), + tickBefore: slot0.tick, + poolPriceBefore: formatPrice(slot0.sqrtPriceX96), + tickAfter: slot0After.tick, + poolPriceAfter: formatPrice(slot0After.sqrtPriceX96), + executionPrice: executionPrice.toPrecision(5), + }).to.matchSnapshot('balances') + }) + } + }) + } +}) diff --git a/lib/uniswap/v3-core/test/UniswapV3Router.spec.ts b/lib/uniswap/v3-core/test/UniswapV3Router.spec.ts new file mode 100644 index 0000000..09af949 --- /dev/null +++ b/lib/uniswap/v3-core/test/UniswapV3Router.spec.ts @@ -0,0 +1,133 @@ +import { Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { TestERC20 } from '../typechain/TestERC20' +import { UniswapV3Factory } from '../typechain/UniswapV3Factory' +import { MockTimeUniswapV3Pool } from '../typechain/MockTimeUniswapV3Pool' +import { expect } from './shared/expect' + +import { poolFixture } from './shared/fixtures' + +import { + FeeAmount, + TICK_SPACINGS, + createPoolFunctions, + PoolFunctions, + createMultiPoolFunctions, + encodePriceSqrt, + getMinTick, + getMaxTick, + expandTo18Decimals, +} from './shared/utilities' +import { TestUniswapV3Router } from '../typechain/TestUniswapV3Router' +import { TestUniswapV3Callee } from '../typechain/TestUniswapV3Callee' + +const feeAmount = FeeAmount.MEDIUM +const tickSpacing = TICK_SPACINGS[feeAmount] + +const createFixtureLoader = waffle.createFixtureLoader + +type ThenArg = T extends PromiseLike ? U : T + +describe('UniswapV3Pool', () => { + let wallet: Wallet, other: Wallet + + let token0: TestERC20 + let token1: TestERC20 + let token2: TestERC20 + let factory: UniswapV3Factory + let pool0: MockTimeUniswapV3Pool + let pool1: MockTimeUniswapV3Pool + + let pool0Functions: PoolFunctions + let pool1Functions: PoolFunctions + + let minTick: number + let maxTick: number + + let swapTargetCallee: TestUniswapV3Callee + let swapTargetRouter: TestUniswapV3Router + + let loadFixture: ReturnType + let createPool: ThenArg>['createPool'] + + before('create fixture loader', async () => { + ;[wallet, other] = await (ethers as any).getSigners() + + loadFixture = createFixtureLoader([wallet, other]) + }) + + beforeEach('deploy first fixture', async () => { + ;({ token0, token1, token2, factory, createPool, swapTargetCallee, swapTargetRouter } = await loadFixture( + poolFixture + )) + + const createPoolWrapped = async ( + amount: number, + spacing: number, + firstToken: TestERC20, + secondToken: TestERC20 + ): Promise<[MockTimeUniswapV3Pool, any]> => { + const pool = await createPool(amount, spacing, firstToken, secondToken) + const poolFunctions = createPoolFunctions({ + swapTarget: swapTargetCallee, + token0: firstToken, + token1: secondToken, + pool, + }) + minTick = getMinTick(spacing) + maxTick = getMaxTick(spacing) + return [pool, poolFunctions] + } + + // default to the 30 bips pool + ;[pool0, pool0Functions] = await createPoolWrapped(feeAmount, tickSpacing, token0, token1) + ;[pool1, pool1Functions] = await createPoolWrapped(feeAmount, tickSpacing, token1, token2) + }) + + it('constructor initializes immutables', async () => { + expect(await pool0.factory()).to.eq(factory.address) + expect(await pool0.token0()).to.eq(token0.address) + expect(await pool0.token1()).to.eq(token1.address) + expect(await pool1.factory()).to.eq(factory.address) + expect(await pool1.token0()).to.eq(token1.address) + expect(await pool1.token1()).to.eq(token2.address) + }) + + describe('multi-swaps', () => { + let inputToken: TestERC20 + let outputToken: TestERC20 + + beforeEach('initialize both pools', async () => { + inputToken = token0 + outputToken = token2 + + await pool0.initialize(encodePriceSqrt(1, 1)) + await pool1.initialize(encodePriceSqrt(1, 1)) + + await pool0Functions.mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) + await pool1Functions.mint(wallet.address, minTick, maxTick, expandTo18Decimals(1)) + }) + + it('multi-swap', async () => { + const token0OfPoolOutput = await pool1.token0() + const ForExact0 = outputToken.address === token0OfPoolOutput + + const { swapForExact0Multi, swapForExact1Multi } = createMultiPoolFunctions({ + inputToken: token0, + swapTarget: swapTargetRouter, + poolInput: pool0, + poolOutput: pool1, + }) + + const method = ForExact0 ? swapForExact0Multi : swapForExact1Multi + + await expect(method(100, wallet.address)) + .to.emit(outputToken, 'Transfer') + .withArgs(pool1.address, wallet.address, 100) + .to.emit(token1, 'Transfer') + .withArgs(pool0.address, pool1.address, 102) + .to.emit(inputToken, 'Transfer') + .withArgs(wallet.address, pool0.address, 104) + }) + }) +}) diff --git a/lib/uniswap/v3-core/test/__snapshots__/BitMath.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/BitMath.spec.ts.snap new file mode 100644 index 0000000..fe68ee6 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/BitMath.spec.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BitMath #leastSignificantBit gas cost of max uint128 1`] = `431`; + +exports[`BitMath #leastSignificantBit gas cost of max uint256 1`] = `431`; + +exports[`BitMath #leastSignificantBit gas cost of smaller number 1`] = `429`; + +exports[`BitMath #mostSignificantBit gas cost of max uint128 1`] = `367`; + +exports[`BitMath #mostSignificantBit gas cost of max uint256 1`] = `385`; + +exports[`BitMath #mostSignificantBit gas cost of smaller number 1`] = `295`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/LiquidityMath.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/LiquidityMath.spec.ts.snap new file mode 100644 index 0000000..6b942a3 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/LiquidityMath.spec.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LiquidityMath #addDelta gas add 1`] = `162`; + +exports[`LiquidityMath #addDelta gas sub 1`] = `176`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/NoDelegateCall.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/NoDelegateCall.spec.ts.snap new file mode 100644 index 0000000..636990a --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/NoDelegateCall.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NoDelegateCall runtime overhead 1`] = `30`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/Oracle.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/Oracle.spec.ts.snap new file mode 100644 index 0000000..cd67254 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/Oracle.spec.ts.snap @@ -0,0 +1,87 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Oracle #grow gas for growing by 1 slot when index != cardinality - 1 1`] = `49081`; + +exports[`Oracle #grow gas for growing by 1 slot when index == cardinality - 1 1`] = `49081`; + +exports[`Oracle #grow gas for growing by 10 slots when index != cardinality - 1 1`] = `249223`; + +exports[`Oracle #grow gas for growing by 10 slots when index == cardinality - 1 1`] = `249223`; + +exports[`Oracle #initialize gas 1`] = `67770`; + +exports[`Oracle #observe before initialization gas for observe since most recent 1`] = `4746`; + +exports[`Oracle #observe before initialization gas for single observation at current time 1`] = `3565`; + +exports[`Oracle #observe before initialization gas for single observation at current time counterfactually computed 1`] = `4067`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 fetch many values 1`] = ` +Object { + "secondsPerLiquidityCumulativeX128s": Array [ + "544451787073501541541399371890829138329", + "799663562264205389138930327464655296921", + "1045423049484883168306923099498710116305", + "1423514568285925905488450441089563684590", + "2152691068830794041481396028443352709138", + "2347138135642758877746181518404363115684", + "2395749902345750086812377890894615717321", + ], + "tickCumulatives": Array [ + -13, + -31, + -43, + -37, + -15, + 9, + 15, + ], +} +`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 gas all of last 20 seconds 1`] = `91193`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 gas between oldest and oldest + 1 1`] = `15811`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 gas latest equal 1`] = `3565`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 gas latest transform 1`] = `4067`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 gas middle 1`] = `13986`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 5 gas oldest 1`] = `15538`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 fetch many values 1`] = ` +Object { + "secondsPerLiquidityCumulativeX128s": Array [ + "544451787073501541541399371890829138329", + "799663562264205389138930327464655296921", + "1045423049484883168306923099498710116305", + "1423514568285925905488450441089563684590", + "2152691068830794041481396028443352709138", + "2347138135642758877746181518404363115684", + "2395749902345750086812377890894615717321", + ], + "tickCumulatives": Array [ + -13, + -31, + -43, + -37, + -15, + 9, + 15, + ], +} +`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 gas all of last 20 seconds 1`] = `91193`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 gas between oldest and oldest + 1 1`] = `15811`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 gas latest equal 1`] = `3565`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 gas latest transform 1`] = `4067`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 gas middle 1`] = `13986`; + +exports[`Oracle #observe initialized with 5 observations with starting time of 4294967291 gas oldest 1`] = `15538`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/SqrtPriceMath.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/SqrtPriceMath.spec.ts.snap new file mode 100644 index 0000000..11308cc --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/SqrtPriceMath.spec.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SqrtPriceMath #getAmount0Delta gas cost for amount0 where roundUp = true 1`] = `610`; + +exports[`SqrtPriceMath #getAmount0Delta gas cost for amount0 where roundUp = true 2`] = `478`; + +exports[`SqrtPriceMath #getAmount1Delta gas cost for amount0 where roundUp = false 1`] = `478`; + +exports[`SqrtPriceMath #getAmount1Delta gas cost for amount0 where roundUp = true 1`] = `610`; + +exports[`SqrtPriceMath #getNextSqrtPriceFromInput zeroForOne = false gas 1`] = `536`; + +exports[`SqrtPriceMath #getNextSqrtPriceFromInput zeroForOne = true gas 1`] = `753`; + +exports[`SqrtPriceMath #getNextSqrtPriceFromOutput zeroForOne = false gas 1`] = `848`; + +exports[`SqrtPriceMath #getNextSqrtPriceFromOutput zeroForOne = true gas 1`] = `441`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/SwapMath.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/SwapMath.spec.ts.snap new file mode 100644 index 0000000..df10cbd --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/SwapMath.spec.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SwapMath #computeSwapStep gas swap one for zero exact in capped 1`] = `2103`; + +exports[`SwapMath #computeSwapStep gas swap one for zero exact in partial 1`] = `2802`; + +exports[`SwapMath #computeSwapStep gas swap one for zero exact out capped 1`] = `1855`; + +exports[`SwapMath #computeSwapStep gas swap one for zero exact out partial 1`] = `2802`; + +exports[`SwapMath #computeSwapStep gas swap zero for one exact in capped 1`] = `2104`; + +exports[`SwapMath #computeSwapStep gas swap zero for one exact in partial 1`] = `3106`; + +exports[`SwapMath #computeSwapStep gas swap zero for one exact out capped 1`] = `1856`; + +exports[`SwapMath #computeSwapStep gas swap zero for one exact out partial 1`] = `3106`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/TickBitmap.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/TickBitmap.spec.ts.snap new file mode 100644 index 0000000..9d040d1 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/TickBitmap.spec.ts.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TickBitmap #flipTick gas cost of flipping a tick that results in deleting a word 1`] = `13427`; + +exports[`TickBitmap #flipTick gas cost of flipping first tick in word to initialized 1`] = `43965`; + +exports[`TickBitmap #flipTick gas cost of flipping second tick in word to initialized 1`] = `26865`; + +exports[`TickBitmap #nextInitializedTickWithinOneWord lte = false gas cost for entire word 1`] = `2627`; + +exports[`TickBitmap #nextInitializedTickWithinOneWord lte = false gas cost just below boundary 1`] = `2627`; + +exports[`TickBitmap #nextInitializedTickWithinOneWord lte = false gas cost on boundary 1`] = `2627`; + +exports[`TickBitmap #nextInitializedTickWithinOneWord lte = true gas cost for entire word 1`] = `2618`; + +exports[`TickBitmap #nextInitializedTickWithinOneWord lte = true gas cost just below boundary 1`] = `2928`; + +exports[`TickBitmap #nextInitializedTickWithinOneWord lte = true gas cost on boundary 1`] = `2618`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/TickMath.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/TickMath.spec.ts.snap new file mode 100644 index 0000000..57e13fd --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/TickMath.spec.ts.snap @@ -0,0 +1,165 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TickMath #getSqrtRatioAtTick tick -50 gas 1`] = `823`; + +exports[`TickMath #getSqrtRatioAtTick tick -50 result 1`] = `"79030349367926598376800521322"`; + +exports[`TickMath #getSqrtRatioAtTick tick -100 gas 1`] = `823`; + +exports[`TickMath #getSqrtRatioAtTick tick -100 result 1`] = `"78833030112140176575862854579"`; + +exports[`TickMath #getSqrtRatioAtTick tick -250 gas 1`] = `865`; + +exports[`TickMath #getSqrtRatioAtTick tick -250 result 1`] = `"78244023372248365697264290337"`; + +exports[`TickMath #getSqrtRatioAtTick tick -500 gas 1`] = `865`; + +exports[`TickMath #getSqrtRatioAtTick tick -500 result 1`] = `"77272108795590369356373805297"`; + +exports[`TickMath #getSqrtRatioAtTick tick -1000 gas 1`] = `865`; + +exports[`TickMath #getSqrtRatioAtTick tick -1000 result 1`] = `"75364347830767020784054125655"`; + +exports[`TickMath #getSqrtRatioAtTick tick -2500 gas 1`] = `851`; + +exports[`TickMath #getSqrtRatioAtTick tick -2500 result 1`] = `"69919044979842180277688105136"`; + +exports[`TickMath #getSqrtRatioAtTick tick -3000 gas 1`] = `879`; + +exports[`TickMath #getSqrtRatioAtTick tick -3000 result 1`] = `"68192822843687888778582228483"`; + +exports[`TickMath #getSqrtRatioAtTick tick -4000 gas 1`] = `865`; + +exports[`TickMath #getSqrtRatioAtTick tick -4000 result 1`] = `"64867181785621769311890333195"`; + +exports[`TickMath #getSqrtRatioAtTick tick -5000 gas 1`] = `851`; + +exports[`TickMath #getSqrtRatioAtTick tick -5000 result 1`] = `"61703726247759831737814779831"`; + +exports[`TickMath #getSqrtRatioAtTick tick -50000 gas 1`] = `865`; + +exports[`TickMath #getSqrtRatioAtTick tick -50000 result 1`] = `"6504256538020985011912221507"`; + +exports[`TickMath #getSqrtRatioAtTick tick -150000 gas 1`] = `893`; + +exports[`TickMath #getSqrtRatioAtTick tick -150000 result 1`] = `"43836292794701720435367485"`; + +exports[`TickMath #getSqrtRatioAtTick tick -250000 gas 1`] = `879`; + +exports[`TickMath #getSqrtRatioAtTick tick -250000 result 1`] = `"295440463448801648376846"`; + +exports[`TickMath #getSqrtRatioAtTick tick -500000 gas 1`] = `879`; + +exports[`TickMath #getSqrtRatioAtTick tick -500000 result 1`] = `"1101692437043807371"`; + +exports[`TickMath #getSqrtRatioAtTick tick -738203 gas 1`] = `911`; + +exports[`TickMath #getSqrtRatioAtTick tick -738203 result 1`] = `"7409801140451"`; + +exports[`TickMath #getSqrtRatioAtTick tick 50 gas 1`] = `863`; + +exports[`TickMath #getSqrtRatioAtTick tick 50 result 1`] = `"79426470787362580746886972461"`; + +exports[`TickMath #getSqrtRatioAtTick tick 100 gas 1`] = `863`; + +exports[`TickMath #getSqrtRatioAtTick tick 100 result 1`] = `"79625275426524748796330556128"`; + +exports[`TickMath #getSqrtRatioAtTick tick 250 gas 1`] = `905`; + +exports[`TickMath #getSqrtRatioAtTick tick 250 result 1`] = `"80224679980005306637834519095"`; + +exports[`TickMath #getSqrtRatioAtTick tick 500 gas 1`] = `905`; + +exports[`TickMath #getSqrtRatioAtTick tick 500 result 1`] = `"81233731461783161732293370115"`; + +exports[`TickMath #getSqrtRatioAtTick tick 1000 gas 1`] = `905`; + +exports[`TickMath #getSqrtRatioAtTick tick 1000 result 1`] = `"83290069058676223003182343270"`; + +exports[`TickMath #getSqrtRatioAtTick tick 2500 gas 1`] = `891`; + +exports[`TickMath #getSqrtRatioAtTick tick 2500 result 1`] = `"89776708723587163891445672585"`; + +exports[`TickMath #getSqrtRatioAtTick tick 3000 gas 1`] = `919`; + +exports[`TickMath #getSqrtRatioAtTick tick 3000 result 1`] = `"92049301871182272007977902845"`; + +exports[`TickMath #getSqrtRatioAtTick tick 4000 gas 1`] = `905`; + +exports[`TickMath #getSqrtRatioAtTick tick 4000 result 1`] = `"96768528593268422080558758223"`; + +exports[`TickMath #getSqrtRatioAtTick tick 5000 gas 1`] = `891`; + +exports[`TickMath #getSqrtRatioAtTick tick 5000 result 1`] = `"101729702841318637793976746270"`; + +exports[`TickMath #getSqrtRatioAtTick tick 50000 gas 1`] = `905`; + +exports[`TickMath #getSqrtRatioAtTick tick 50000 result 1`] = `"965075977353221155028623082916"`; + +exports[`TickMath #getSqrtRatioAtTick tick 150000 gas 1`] = `933`; + +exports[`TickMath #getSqrtRatioAtTick tick 150000 result 1`] = `"143194173941309278083010301478497"`; + +exports[`TickMath #getSqrtRatioAtTick tick 250000 gas 1`] = `919`; + +exports[`TickMath #getSqrtRatioAtTick tick 250000 result 1`] = `"21246587762933397357449903968194344"`; + +exports[`TickMath #getSqrtRatioAtTick tick 500000 gas 1`] = `919`; + +exports[`TickMath #getSqrtRatioAtTick tick 500000 result 1`] = `"5697689776495288729098254600827762987878"`; + +exports[`TickMath #getSqrtRatioAtTick tick 738203 gas 1`] = `951`; + +exports[`TickMath #getSqrtRatioAtTick tick 738203 result 1`] = `"847134979253254120489401328389043031315994541"`; + +exports[`TickMath #getTickAtSqrtRatio ratio 4295128739 gas 1`] = `2371`; + +exports[`TickMath #getTickAtSqrtRatio ratio 4295128739 result 1`] = `-887272`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543 gas 1`] = `2367`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543 result 1`] = `-276325`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950 gas 1`] = `2367`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950 result 1`] = `-138163`; + +exports[`TickMath #getTickAtSqrtRatio ratio 9903520314283042199192993792 gas 1`] = `1389`; + +exports[`TickMath #getTickAtSqrtRatio ratio 9903520314283042199192993792 result 1`] = `-41591`; + +exports[`TickMath #getTickAtSqrtRatio ratio 28011385487393069959365969113 gas 1`] = `2329`; + +exports[`TickMath #getTickAtSqrtRatio ratio 28011385487393069959365969113 result 1`] = `-20796`; + +exports[`TickMath #getTickAtSqrtRatio ratio 56022770974786139918731938227 gas 1`] = `2315`; + +exports[`TickMath #getTickAtSqrtRatio ratio 56022770974786139918731938227 result 1`] = `-6932`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950336 gas 1`] = `2235`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950336 result 1`] = `0`; + +exports[`TickMath #getTickAtSqrtRatio ratio 112045541949572279837463876454 gas 1`] = `2355`; + +exports[`TickMath #getTickAtSqrtRatio ratio 112045541949572279837463876454 result 1`] = `6931`; + +exports[`TickMath #getTickAtSqrtRatio ratio 224091083899144559674927752909 gas 1`] = `2369`; + +exports[`TickMath #getTickAtSqrtRatio ratio 224091083899144559674927752909 result 1`] = `20795`; + +exports[`TickMath #getTickAtSqrtRatio ratio 633825300114114700748351602688 gas 1`] = `2393`; + +exports[`TickMath #getTickAtSqrtRatio ratio 633825300114114700748351602688 result 1`] = `41590`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950336000 gas 1`] = `2407`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950336000 result 1`] = `138162`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950336000000 gas 1`] = `2407`; + +exports[`TickMath #getTickAtSqrtRatio ratio 79228162514264337593543950336000000 result 1`] = `276324`; + +exports[`TickMath #getTickAtSqrtRatio ratio 1461446703485210103287273052203988822378723970341 gas 1`] = `2431`; + +exports[`TickMath #getTickAtSqrtRatio ratio 1461446703485210103287273052203988822378723970341 result 1`] = `887271`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Factory.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Factory.spec.ts.snap new file mode 100644 index 0000000..109b0c9 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Factory.spec.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UniswapV3Factory #createPool gas 1`] = `4557092`; + +exports[`UniswapV3Factory factory bytecode size 1`] = `24535`; + +exports[`UniswapV3Factory pool bytecode size 1`] = `22142`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.arbitrage.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.arbitrage.spec.ts.snap new file mode 100644 index 0000000..2e4e465 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.arbitrage.spec.ts.snap @@ -0,0 +1,977 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9699", + "arbBalanceDelta1": "-0.0099043", + "backrun": Object { + "delta0": "-9.9699", + "delta1": "0.0099043", + "executionPrice": "0.00099342", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "9.7606", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.0099900", + "executionPrice": "0.00099900", + "priceAfter": "0.0000010040", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9990", + "arbBalanceDelta1": "-0.0099044", + "backrun": Object { + "delta0": "-0.30578", + "delta1": "0.0095969", + "executionPrice": "0.031385", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0910e+12", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "0.30682", + "delta1": "-0.0096834", + "executionPrice": "0.031561", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0910e+12", + }, + "profit": Object { + "afterFrontrun": "-0.29100", + "afterSandwich": "9.4990", + "final": "9.7891", + }, + "sandwichedPrice": "0.00099910", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9700", + "arbBalanceDelta1": "-0.010055", + "backrun": Object { + "delta0": "-9.9700", + "delta1": "0.010055", + "executionPrice": "0.0010085", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "10.060", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.0099900", + "executionPrice": "0.00099900", + "priceAfter": "0.0000010040", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9991", + "arbBalanceDelta1": "-0.010055", + "backrun": Object { + "delta0": "-0.30593", + "delta1": "0.0097475", + "executionPrice": "0.031862", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0910e+12", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "0.30682", + "delta1": "-0.0096834", + "executionPrice": "0.031561", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0910e+12", + }, + "profit": Object { + "afterFrontrun": "-0.30020", + "afterSandwich": "9.7898", + "final": "10.089", + }, + "sandwichedPrice": "0.00099910", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9583", + "arbBalanceDelta1": "-0.90001", + "backrun": Object { + "delta0": "-9.9583", + "delta1": "0.90001", + "executionPrice": "0.090377", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "8.8592", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.90884", + "executionPrice": "0.090884", + "priceAfter": "0.0083097", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9814", + "arbBalanceDelta1": "-0.90054", + "backrun": Object { + "delta0": "-2.2983", + "delta1": "0.68841", + "executionPrice": "0.29953", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0412e+13", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "2.3169", + "delta1": "-0.69788", + "executionPrice": "0.30121", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0412e+13", + }, + "profit": Object { + "afterFrontrun": "-1.5727", + "afterSandwich": "7.3173", + "final": "8.8812", + }, + "sandwichedPrice": "0.091001", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9735", + "arbBalanceDelta1": "-0.91507", + "backrun": Object { + "delta0": "-9.9735", + "delta1": "0.91507", + "executionPrice": "0.091750", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "9.1581", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.90884", + "executionPrice": "0.090884", + "priceAfter": "0.0083097", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9965", + "arbBalanceDelta1": "-0.91560", + "backrun": Object { + "delta0": "-2.3134", + "delta1": "0.70347", + "executionPrice": "0.30408", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0412e+13", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "2.3169", + "delta1": "-0.69788", + "executionPrice": "0.30121", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0412e+13", + }, + "profit": Object { + "afterFrontrun": "-1.6422", + "afterSandwich": "7.5478", + "final": "9.1809", + }, + "sandwichedPrice": "0.091001", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.8533", + "arbBalanceDelta1": "-4.8918", + "backrun": Object { + "delta0": "-9.8533", + "delta1": "4.8918", + "executionPrice": "0.49646", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "4.7644", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-4.9925", + "executionPrice": "0.49925", + "priceAfter": "0.25075", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.8709", + "arbBalanceDelta1": "-4.8940", + "backrun": Object { + "delta0": "-4.0029", + "delta1": "2.8107", + "executionPrice": "0.70217", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "2.4408e+13", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "4.1321", + "delta1": "-2.9177", + "executionPrice": "0.70611", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "2.4408e+13", + }, + "profit": Object { + "afterFrontrun": "-1.1317", + "afterSandwich": "3.6674", + "final": "4.7795", + }, + "sandwichedPrice": "0.50009", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "10.005", + "arbBalanceDelta1": "-5.0424", + "backrun": Object { + "delta0": "-10.005", + "delta1": "5.0424", + "executionPrice": "0.50401", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "5.0623", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-4.9925", + "executionPrice": "0.49925", + "priceAfter": "0.25075", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "10.022", + "arbBalanceDelta1": "-5.0446", + "backrun": Object { + "delta0": "-4.1543", + "delta1": "2.9613", + "executionPrice": "0.71283", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "2.4408e+13", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "4.1321", + "delta1": "-2.9177", + "executionPrice": "0.70611", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "2.4408e+13", + }, + "profit": Object { + "afterFrontrun": "-1.2557", + "afterSandwich": "3.8434", + "final": "5.0779", + }, + "sandwichedPrice": "0.50009", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "8.8029", + "arbBalanceDelta1": "-7.9363", + "backrun": Object { + "delta0": "-8.8029", + "delta1": "7.9363", + "executionPrice": "0.90155", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "0.69056", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-9.0661", + "executionPrice": "0.90661", + "priceAfter": "0.82690", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "8.8190", + "arbBalanceDelta1": "-7.9680", + "backrun": Object { + "delta0": "-3.4354", + "delta1": "3.2562", + "executionPrice": "0.94781", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "3.2947e+13", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "4.6164", + "delta1": "-4.4000", + "executionPrice": "0.95313", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "3.2947e+13", + }, + "profit": Object { + "afterFrontrun": "-0.12404", + "afterSandwich": "0.56403", + "final": "0.67460", + }, + "sandwichedPrice": "0.91119", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "10.317", + "arbBalanceDelta1": "-9.4423", + "backrun": Object { + "delta0": "-10.317", + "delta1": "9.4423", + "executionPrice": "0.91525", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "0.97752", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-9.0661", + "executionPrice": "0.90661", + "priceAfter": "0.82690", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 0; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "10.333", + "arbBalanceDelta1": "-9.4741", + "backrun": Object { + "delta0": "-4.9492", + "delta1": "4.7622", + "executionPrice": "0.96221", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "3.2947e+13", + }, + "collect": Object { + "amount0": "0.030000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "4.6164", + "delta1": "-4.4000", + "executionPrice": "0.95313", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "3.2947e+13", + }, + "profit": Object { + "afterFrontrun": "-0.26253", + "afterSandwich": "0.72554", + "final": "0.96205", + }, + "sandwichedPrice": "0.91119", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9699", + "arbBalanceDelta1": "-0.0099043", + "backrun": Object { + "delta0": "-9.9699", + "delta1": "0.0099043", + "executionPrice": "0.00099342", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "9.7606", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.0099900", + "executionPrice": "0.00099900", + "priceAfter": "0.0000010040", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9940", + "arbBalanceDelta1": "-0.0099044", + "backrun": Object { + "delta0": "-0.30578", + "delta1": "0.0095969", + "executionPrice": "0.031385", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0910e+12", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "0.30682", + "delta1": "-0.0096834", + "executionPrice": "0.031561", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0910e+12", + }, + "profit": Object { + "afterFrontrun": "-0.29100", + "afterSandwich": "9.4941", + "final": "9.7842", + }, + "sandwichedPrice": "0.00099910", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9700", + "arbBalanceDelta1": "-0.010055", + "backrun": Object { + "delta0": "-9.9700", + "delta1": "0.010055", + "executionPrice": "0.0010085", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "10.060", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.0099900", + "executionPrice": "0.00099900", + "priceAfter": "0.0000010040", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 0.010000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9941", + "arbBalanceDelta1": "-0.010055", + "backrun": Object { + "delta0": "-0.30593", + "delta1": "0.0097475", + "executionPrice": "0.031862", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0910e+12", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "0.30682", + "delta1": "-0.0096834", + "executionPrice": "0.031561", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0910e+12", + }, + "profit": Object { + "afterFrontrun": "-0.30020", + "afterSandwich": "9.7848", + "final": "10.084", + }, + "sandwichedPrice": "0.00099910", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9583", + "arbBalanceDelta1": "-0.90001", + "backrun": Object { + "delta0": "-9.9583", + "delta1": "0.90001", + "executionPrice": "0.090377", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "8.8592", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.90884", + "executionPrice": "0.090884", + "priceAfter": "0.0083097", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9764", + "arbBalanceDelta1": "-0.90054", + "backrun": Object { + "delta0": "-2.2983", + "delta1": "0.68841", + "executionPrice": "0.29953", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0412e+13", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "2.3169", + "delta1": "-0.69788", + "executionPrice": "0.30121", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0412e+13", + }, + "profit": Object { + "afterFrontrun": "-1.5727", + "afterSandwich": "7.3124", + "final": "8.8763", + }, + "sandwichedPrice": "0.091001", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.9735", + "arbBalanceDelta1": "-0.91507", + "backrun": Object { + "delta0": "-9.9735", + "delta1": "0.91507", + "executionPrice": "0.091750", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "9.1581", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-0.90884", + "executionPrice": "0.090884", + "priceAfter": "0.0083097", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 1.0000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.9915", + "arbBalanceDelta1": "-0.91560", + "backrun": Object { + "delta0": "-2.3134", + "delta1": "0.70347", + "executionPrice": "0.30408", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "1.0412e+13", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "2.3169", + "delta1": "-0.69788", + "executionPrice": "0.30121", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "1.0412e+13", + }, + "profit": Object { + "afterFrontrun": "-1.6422", + "afterSandwich": "7.5427", + "final": "9.1758", + }, + "sandwichedPrice": "0.091001", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "9.8533", + "arbBalanceDelta1": "-4.8918", + "backrun": Object { + "delta0": "-9.8533", + "delta1": "4.8918", + "executionPrice": "0.49646", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "4.7644", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-4.9925", + "executionPrice": "0.49925", + "priceAfter": "0.25075", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "9.8659", + "arbBalanceDelta1": "-4.8940", + "backrun": Object { + "delta0": "-4.0029", + "delta1": "2.8107", + "executionPrice": "0.70217", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "2.4408e+13", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "4.1321", + "delta1": "-2.9177", + "executionPrice": "0.70611", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "2.4408e+13", + }, + "profit": Object { + "afterFrontrun": "-1.1317", + "afterSandwich": "3.6625", + "final": "4.7746", + }, + "sandwichedPrice": "0.50009", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "10.005", + "arbBalanceDelta1": "-5.0424", + "backrun": Object { + "delta0": "-10.005", + "delta1": "5.0424", + "executionPrice": "0.50401", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "5.0623", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-4.9925", + "executionPrice": "0.49925", + "priceAfter": "0.25075", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 10.000 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "10.017", + "arbBalanceDelta1": "-5.0446", + "backrun": Object { + "delta0": "-4.1543", + "delta1": "2.9613", + "executionPrice": "0.71283", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "2.4408e+13", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "4.1321", + "delta1": "-2.9177", + "executionPrice": "0.70611", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "2.4408e+13", + }, + "profit": Object { + "afterFrontrun": "-1.2557", + "afterSandwich": "3.8384", + "final": "5.0729", + }, + "sandwichedPrice": "0.50009", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "8.8029", + "arbBalanceDelta1": "-7.9363", + "backrun": Object { + "delta0": "-8.8029", + "delta1": "7.9363", + "executionPrice": "0.90155", + }, + "finalPrice": "0.97706", + "profit": Object { + "final": "0.69056", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-9.0661", + "executionPrice": "0.90661", + "priceAfter": "0.82690", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 0.98 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "8.8140", + "arbBalanceDelta1": "-7.9680", + "backrun": Object { + "delta0": "-3.4354", + "delta1": "3.2562", + "executionPrice": "0.94781", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "3.2947e+13", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "0.97706", + "frontrun": Object { + "delta0": "4.6164", + "delta1": "-4.4000", + "executionPrice": "0.95313", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "3.2947e+13", + }, + "profit": Object { + "afterFrontrun": "-0.12404", + "afterSandwich": "0.55913", + "final": "0.66970", + }, + "sandwichedPrice": "0.91119", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 backrun to true price after swap only 1`] = ` +Object { + "arbBalanceDelta0": "10.317", + "arbBalanceDelta1": "-9.4423", + "backrun": Object { + "delta0": "-10.317", + "delta1": "9.4423", + "executionPrice": "0.91525", + }, + "finalPrice": "1.0070", + "profit": Object { + "final": "0.97752", + }, +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 not sandwiched 1`] = ` +Object { + "amount0Delta": "10.000", + "amount1Delta": "-9.0661", + "executionPrice": "0.90661", + "priceAfter": "0.82690", +} +`; + +exports[`UniswapV3Pool arbitrage tests protocol fee = 6; passive liquidity of 100.00 exact input of 10e18 token0 with starting price of 1.0 and true price of 1.01 sandwiched with swap to execution price then mint max liquidity/target/burn max liquidity 1`] = ` +Object { + "arbBalanceDelta0": "10.328", + "arbBalanceDelta1": "-9.4741", + "backrun": Object { + "delta0": "-4.9492", + "delta1": "4.7622", + "executionPrice": "0.96221", + }, + "burn": Object { + "amount0": "9.9700", + "amount1": "3.2947e+13", + }, + "collect": Object { + "amount0": "0.025000", + "amount1": "0.0000", + }, + "finalPrice": "1.0070", + "frontrun": Object { + "delta0": "4.6164", + "delta1": "-4.4000", + "executionPrice": "0.95313", + }, + "mint": Object { + "amount0": "0.0000", + "amount1": "3.2947e+13", + }, + "profit": Object { + "afterFrontrun": "-0.26253", + "afterSandwich": "0.72049", + "final": "0.95700", + }, + "sandwichedPrice": "0.91119", +} +`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.gas.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.gas.spec.ts.snap new file mode 100644 index 0000000..37bc657 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.gas.spec.ts.snap @@ -0,0 +1,169 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UniswapV3Pool gas tests fee is off #burn above current price burn entire position after some time passes 1`] = `58703`; + +exports[`UniswapV3Pool gas tests fee is off #burn above current price burn when only position using ticks 1`] = `58703`; + +exports[`UniswapV3Pool gas tests fee is off #burn above current price entire position burn but other positions are using the ticks 1`] = `82740`; + +exports[`UniswapV3Pool gas tests fee is off #burn above current price partial position burn 1`] = `97740`; + +exports[`UniswapV3Pool gas tests fee is off #burn around current price burn entire position after some time passes 1`] = `69498`; + +exports[`UniswapV3Pool gas tests fee is off #burn around current price burn when only position using ticks 1`] = `66309`; + +exports[`UniswapV3Pool gas tests fee is off #burn around current price entire position burn but other positions are using the ticks 1`] = `87551`; + +exports[`UniswapV3Pool gas tests fee is off #burn around current price partial position burn 1`] = `102551`; + +exports[`UniswapV3Pool gas tests fee is off #burn below current price burn entire position after some time passes 1`] = `64624`; + +exports[`UniswapV3Pool gas tests fee is off #burn below current price burn when only position using ticks 1`] = `64624`; + +exports[`UniswapV3Pool gas tests fee is off #burn below current price entire position burn but other positions are using the ticks 1`] = `83381`; + +exports[`UniswapV3Pool gas tests fee is off #burn below current price partial position burn 1`] = `98381`; + +exports[`UniswapV3Pool gas tests fee is off #collect close to worst case 1`] = `35816`; + +exports[`UniswapV3Pool gas tests fee is off #increaseObservationCardinalityNext grow by 1 slot 1`] = `51098`; + +exports[`UniswapV3Pool gas tests fee is off #increaseObservationCardinalityNext no op 1`] = `24677`; + +exports[`UniswapV3Pool gas tests fee is off #mint above current price add to position after some time passes 1`] = `109351`; + +exports[`UniswapV3Pool gas tests fee is off #mint above current price add to position existing 1`] = `109351`; + +exports[`UniswapV3Pool gas tests fee is off #mint above current price new position mint first in range 1`] = `228025`; + +exports[`UniswapV3Pool gas tests fee is off #mint above current price second position in same range 1`] = `126451`; + +exports[`UniswapV3Pool gas tests fee is off #mint around current price add to position after some time passes 1`] = `147718`; + +exports[`UniswapV3Pool gas tests fee is off #mint around current price add to position existing 1`] = `138539`; + +exports[`UniswapV3Pool gas tests fee is off #mint around current price new position mint first in range 1`] = `328848`; + +exports[`UniswapV3Pool gas tests fee is off #mint around current price second position in same range 1`] = `155639`; + +exports[`UniswapV3Pool gas tests fee is off #mint below current price add to position after some time passes 1`] = `109943`; + +exports[`UniswapV3Pool gas tests fee is off #mint below current price add to position existing 1`] = `109943`; + +exports[`UniswapV3Pool gas tests fee is off #mint below current price new position mint first in range 1`] = `309729`; + +exports[`UniswapV3Pool gas tests fee is off #mint below current price second position in same range 1`] = `127043`; + +exports[`UniswapV3Pool gas tests fee is off #poke best case 1`] = `52006`; + +exports[`UniswapV3Pool gas tests fee is off #snapshotCumulativesInside tick above 1`] = `29643`; + +exports[`UniswapV3Pool gas tests fee is off #snapshotCumulativesInside tick below 1`] = `29605`; + +exports[`UniswapV3Pool gas tests fee is off #snapshotCumulativesInside tick inside 1`] = `37053`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `114858`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 first swap in block with no tick movement 1`] = `99004`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `129871`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `152706`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `129469`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `152706`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `212706`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `114858`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 second swap in block with no tick movement 1`] = `99115`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `116454`; + +exports[`UniswapV3Pool gas tests fee is off #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `139267`; + +exports[`UniswapV3Pool gas tests fee is on #burn above current price burn entire position after some time passes 1`] = `58703`; + +exports[`UniswapV3Pool gas tests fee is on #burn above current price burn when only position using ticks 1`] = `58703`; + +exports[`UniswapV3Pool gas tests fee is on #burn above current price entire position burn but other positions are using the ticks 1`] = `82740`; + +exports[`UniswapV3Pool gas tests fee is on #burn above current price partial position burn 1`] = `97740`; + +exports[`UniswapV3Pool gas tests fee is on #burn around current price burn entire position after some time passes 1`] = `69498`; + +exports[`UniswapV3Pool gas tests fee is on #burn around current price burn when only position using ticks 1`] = `66309`; + +exports[`UniswapV3Pool gas tests fee is on #burn around current price entire position burn but other positions are using the ticks 1`] = `87551`; + +exports[`UniswapV3Pool gas tests fee is on #burn around current price partial position burn 1`] = `102551`; + +exports[`UniswapV3Pool gas tests fee is on #burn below current price burn entire position after some time passes 1`] = `64624`; + +exports[`UniswapV3Pool gas tests fee is on #burn below current price burn when only position using ticks 1`] = `64624`; + +exports[`UniswapV3Pool gas tests fee is on #burn below current price entire position burn but other positions are using the ticks 1`] = `83381`; + +exports[`UniswapV3Pool gas tests fee is on #burn below current price partial position burn 1`] = `98381`; + +exports[`UniswapV3Pool gas tests fee is on #collect close to worst case 1`] = `35816`; + +exports[`UniswapV3Pool gas tests fee is on #increaseObservationCardinalityNext grow by 1 slot 1`] = `51098`; + +exports[`UniswapV3Pool gas tests fee is on #increaseObservationCardinalityNext no op 1`] = `24677`; + +exports[`UniswapV3Pool gas tests fee is on #mint above current price add to position after some time passes 1`] = `109363`; + +exports[`UniswapV3Pool gas tests fee is on #mint above current price add to position existing 1`] = `109363`; + +exports[`UniswapV3Pool gas tests fee is on #mint above current price new position mint first in range 1`] = `228037`; + +exports[`UniswapV3Pool gas tests fee is on #mint above current price second position in same range 1`] = `126463`; + +exports[`UniswapV3Pool gas tests fee is on #mint around current price add to position after some time passes 1`] = `147730`; + +exports[`UniswapV3Pool gas tests fee is on #mint around current price add to position existing 1`] = `138551`; + +exports[`UniswapV3Pool gas tests fee is on #mint around current price new position mint first in range 1`] = `328860`; + +exports[`UniswapV3Pool gas tests fee is on #mint around current price second position in same range 1`] = `155651`; + +exports[`UniswapV3Pool gas tests fee is on #mint below current price add to position after some time passes 1`] = `109955`; + +exports[`UniswapV3Pool gas tests fee is on #mint below current price add to position existing 1`] = `109955`; + +exports[`UniswapV3Pool gas tests fee is on #mint below current price new position mint first in range 1`] = `309741`; + +exports[`UniswapV3Pool gas tests fee is on #mint below current price second position in same range 1`] = `127055`; + +exports[`UniswapV3Pool gas tests fee is on #poke best case 1`] = `52006`; + +exports[`UniswapV3Pool gas tests fee is on #snapshotCumulativesInside tick above 1`] = `29643`; + +exports[`UniswapV3Pool gas tests fee is on #snapshotCumulativesInside tick below 1`] = `29605`; + +exports[`UniswapV3Pool gas tests fee is on #snapshotCumulativesInside tick inside 1`] = `37053`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 first swap in block moves tick, no initialized crossings 1`] = `120257`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 first swap in block with no tick movement 1`] = `104256`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing a single initialized tick 1`] = `135417`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 first swap in block, large swap crossing several initialized ticks 1`] = `158693`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 first swap in block, large swap, no initialized crossings 1`] = `135162`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks after some time passes 1`] = `158693`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 large swap crossing several initialized ticks second time after some time passes 1`] = `218693`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 second swap in block moves tick, no initialized crossings 1`] = `120257`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 second swap in block with no tick movement 1`] = `104367`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing a single initialized tick 1`] = `121853`; + +exports[`UniswapV3Pool gas tests fee is on #swapExact0For1 second swap in block, large swap crossing several initialized ticks 1`] = `145107`; diff --git a/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.swaps.spec.ts.snap b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.swaps.spec.ts.snap new file mode 100644 index 0000000..e36c7c0 --- /dev/null +++ b/lib/uniswap/v3-core/test/__snapshots__/UniswapV3Pool.swaps.spec.ts.snap @@ -0,0 +1,3541 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UniswapV3Pool swap tests close to max price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "1000", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-26083549850867114346332688477747755628", + "executionPrice": "2.6084e+34", + "feeGrowthGlobal0X128Delta": "2381976568446569244235", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "4.1734e+30", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 705098, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "0", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "1000", + "executionPrice": "-Infinity", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "1.7014e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 880340, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "1000000000000000000", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-26087635650665564420687107504180041533", + "executionPrice": "2.6088e+19", + "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "4.0241", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 13923, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "1000000000000000000", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-26087635650665564420687107504180041533", + "executionPrice": "2.6088e+19", + "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "4.0241", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 13923, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "0", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "1000000000000000000", + "executionPrice": "-Infinity", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", + "poolPriceAfter": "1.7014e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 880340, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "poolBalance0": "1", + "poolBalance1": "26087635650665564424699143612505016738", + "poolPriceBefore": "1.7014e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "2", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-1000", + "executionPrice": "500.00", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.7014e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 880340, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "2", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-1000000000000000000", + "executionPrice": "5.0000e+17", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.7014e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 880340, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "2", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-1000000000000000000", + "executionPrice": "5.0000e+17", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.7014e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 880340, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "3171793039286238109", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-26087635650665564423434232548437664977", + "executionPrice": "8.2249e+18", + "feeGrowthGlobal0X128Delta": "1618957864187523123655042148763283097", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.7014e+38", + "tickAfter": -9164, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token0 for token1 to price 2.5000 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "1268717215714495281", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "-26087635650665564421536865952336637378", + "executionPrice": "2.0562e+19", + "feeGrowthGlobal0X128Delta": "647583145675012618257449376796101507", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 9163, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "0", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "10740898373457544742072477595619363803", + "executionPrice": "-Infinity", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "5482407482066087054477299856254072312542046383926535301", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 887271, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "1", + "amount0Delta": "0", + "amount1Before": "26087635650665564424699143612505016738", + "amount1Delta": "10740898373457544742072477595619363803", + "executionPrice": "-Infinity", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "5482407482066087054477299856254072312542046383926535301", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.7014e+38", + "tickAfter": 887271, + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "poolBalance0": "1", + "poolBalance1": "26087635650665564424699143612505016738", + "poolPriceBefore": "1.7014e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "1", + "poolBalance1": "26087635650665564424699143612505016738", + "poolPriceBefore": "1.7014e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to max price swap token1 for token0 to price 2.5000 1`] = ` +Object { + "poolBalance0": "1", + "poolBalance1": "26087635650665564424699143612505016738", + "poolPriceBefore": "1.7014e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 880340, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "1000", + "amount1Before": "1", + "amount1Delta": "0", + "executionPrice": "0.0000", + "feeGrowthGlobal0X128Delta": "170141183460469231731687", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000059000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -880303, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-26033697540846965126433148994127431276", + "amount1Before": "1", + "amount1Delta": "1000", + "executionPrice": "0.000000000000000000000000000000000038412", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "2381976568446569244235", + "poolPriceAfter": "0.00000000000000000000000000000023974", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -705093, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "1000000000000000000", + "amount1Before": "1", + "amount1Delta": "0", + "executionPrice": "0.0000", + "feeGrowthGlobal0X128Delta": "170141183460469231731687303715884105728", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000059000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -880303, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "poolBalance0": "26037782196502120275425782622539039026", + "poolBalance1": "1", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-26037782196502120271413746514214063808", + "amount1Before": "1", + "amount1Delta": "1000000000000000000", + "executionPrice": "0.000000000000000000038406", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", + "poolPriceAfter": "0.24850", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -13924, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-26037782196502120271413746514214063808", + "amount1Before": "1", + "amount1Delta": "1000000000000000000", + "executionPrice": "0.000000000000000000038406", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", + "poolPriceAfter": "0.24850", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -13924, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "10790901831095468191587263901270792610", + "amount1Before": "1", + "amount1Delta": "0", + "executionPrice": "0.0000", + "feeGrowthGlobal0X128Delta": "5507930424444982259736347157352787128931407551935325049", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -887272, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "10790901831095468191587263901270792610", + "amount1Before": "1", + "amount1Delta": "0", + "executionPrice": "0.0000", + "feeGrowthGlobal0X128Delta": "5507930424444982259736347157352787128931407551935325049", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -887272, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "poolBalance0": "26037782196502120275425782622539039026", + "poolBalance1": "1", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token0 for token1 to price 0.40000 1`] = ` +Object { + "poolBalance0": "26037782196502120275425782622539039026", + "poolBalance1": "1", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "26037782196502120275425782622539039026", + "poolBalance1": "1", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-1000", + "amount1Before": "1", + "amount1Delta": "2", + "executionPrice": "0.0020000", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.0000000000000000000000000000000000000059000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -880303, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-1000000000000000000", + "amount1Before": "1", + "amount1Delta": "2", + "executionPrice": "0.0000000000000000020000", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.0000000000000000000000000000000000000059000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -880303, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-1000000000000000000", + "amount1Before": "1", + "amount1Delta": "2", + "executionPrice": "0.0000000000000000020000", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.0000000000000000000000000000000000000059000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -880303, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token1 for token0 to price 0.40000 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-26037782196502120272263504962370659661", + "amount1Before": "1", + "amount1Delta": "1268717215714495283", + "executionPrice": "0.000000000000000000048726", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "647583145675012958539816297734564973", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": -9164, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests close to min price swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "26037782196502120275425782622539039026", + "amount0Delta": "-26037782196502120274160871558471687260", + "amount1Before": "1", + "amount1Delta": "3171793039286238112", + "executionPrice": "0.00000000000000000012182", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1618957864187523634078592530170978294", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "0.0000000000000000000000000000000000000059000", + "tickAfter": 9163, + "tickBefore": -880303, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1000", + "amount1Before": "2000000000000000000", + "amount1Delta": "-989", + "executionPrice": "0.98900", + "feeGrowthGlobal0X128Delta": "1701411834604692317316", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-989", + "amount1Before": "2000000000000000000", + "amount1Delta": "1000", + "executionPrice": "1.0111", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1701411834604692317316", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1000000000000000000", + "amount1Before": "2000000000000000000", + "amount1Delta": "-662207357859531772", + "executionPrice": "0.66221", + "feeGrowthGlobal0X128Delta": "1701411834604692317316873037158841057", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.44742", + "poolPriceBefore": "1.0000", + "tickAfter": -8043, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "836795075501202120", + "amount1Before": "2000000000000000000", + "amount1Delta": "-585786437626904951", + "executionPrice": "0.70004", + "feeGrowthGlobal0X128Delta": "1423733044596672457631004491657125052", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-662207357859531772", + "amount1Before": "2000000000000000000", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.5101", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1701411834604692317316873037158841057", + "poolPriceAfter": "2.2350", + "poolPriceBefore": "1.0000", + "tickAfter": 8042, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-585786437626904951", + "amount1Before": "2000000000000000000", + "amount1Delta": "836795075501202120", + "executionPrice": "1.4285", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1423733044596672457631004491657125052", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1012", + "amount1Before": "2000000000000000000", + "amount1Delta": "-1000", + "executionPrice": "0.98814", + "feeGrowthGlobal0X128Delta": "1871553018065161549048", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "2020202020202020203", + "amount1Before": "2000000000000000000", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.49500", + "feeGrowthGlobal0X128Delta": "3437195625464025050172418213103875650", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.25000", + "poolPriceBefore": "1.0000", + "tickAfter": -13864, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "836795075501202120", + "amount1Before": "2000000000000000000", + "amount1Delta": "-585786437626904951", + "executionPrice": "0.70004", + "feeGrowthGlobal0X128Delta": "1423733044596672457631004491657125052", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1174017838553918518", + "amount1Before": "2000000000000000000", + "amount1Delta": "-735088935932648267", + "executionPrice": "0.62613", + "feeGrowthGlobal0X128Delta": "1997487844552658120479227965844634309", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "2000000000000000000", + "poolBalance1": "2000000000000000000", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-1000", + "amount1Before": "2000000000000000000", + "amount1Delta": "1012", + "executionPrice": "1.0120", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1871553018065161549048", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-1000000000000000000", + "amount1Before": "2000000000000000000", + "amount1Delta": "2020202020202020203", + "executionPrice": "2.0202", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "3437195625464025050172418213103875650", + "poolPriceAfter": "4.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 13863, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-585786437626904951", + "amount1Before": "2000000000000000000", + "amount1Delta": "836795075501202120", + "executionPrice": "1.4285", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1423733044596672457631004491657125052", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "2000000000000000000", + "poolBalance1": "2000000000000000000", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests high fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-735088935932648267", + "amount1Before": "2000000000000000000", + "amount1Delta": "1174017838553918518", + "executionPrice": "1.5971", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1997487844552658120479227965844634309", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1000", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-36792225529204286454178948640580261338", + "executionPrice": "3.6792e+34", + "feeGrowthGlobal0X128Delta": "2381976568446569244235", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "4.1734e+30", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 705098, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1000000000000000000", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-36796311329002736528533367667012547243", + "executionPrice": "3.6796e+19", + "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "4.0241", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 13923, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1000000000000000000", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-36796311329002736528533367667012547243", + "executionPrice": "3.6796e+19", + "feeGrowthGlobal0X128Delta": "510423550381413479995299567101531162", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "4.0241", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 13923, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "2", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-1000", + "executionPrice": "500.00", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.3849e+38", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 887219, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "2", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-1000000000000000000", + "executionPrice": "5.0000e+17", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.3849e+38", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 887219, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "2", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-1000000000000000000", + "executionPrice": "5.0000e+17", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.3849e+38", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 887219, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "3171793039286238109", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-36796311329002736531280492711270170687", + "executionPrice": "1.1601e+19", + "feeGrowthGlobal0X128Delta": "1618957864187523123655042148763283097", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "3.4026e+38", + "tickAfter": -9164, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token0 for token1 to price 2.5000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1268717215714495281", + "amount1Before": "36796311329002736532545403775337522448", + "amount1Delta": "-36796311329002736529383126115169143088", + "executionPrice": "2.9003e+19", + "feeGrowthGlobal0X128Delta": "647583145675012618257449376796101507", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "3.4026e+38", + "tickAfter": 9163, + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the max ratio swap token1 for token0 to price 2.5000 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "36796311329002736532545403775337522448", + "poolPriceBefore": "3.4026e+38", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 887271, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-36792226666449146913445651103694411508", + "amount1Before": "0", + "amount1Delta": "1000", + "executionPrice": "0.000000000000000000000000000000000027180", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "2381976568446569244235", + "poolPriceAfter": "0.00000000000000000000000000000023974", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -705093, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-36796311322104302058426248623781044040", + "amount1Before": "0", + "amount1Delta": "1000000000000000000", + "executionPrice": "0.000000000000000000027177", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", + "poolPriceAfter": "0.24850", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -13924, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-36796311322104302058426248623781044040", + "amount1Before": "0", + "amount1Delta": "1000000000000000000", + "executionPrice": "0.000000000000000000027177", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381413820277666488039994629", + "poolPriceAfter": "0.24850", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -13924, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token0 for token1 to price 0.40000 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "36796311322104302062438284732106019258", + "poolBalance1": "0", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-1000", + "amount1Before": "0", + "amount1Delta": "2", + "executionPrice": "0.0020000", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.0000000000000000000000000000000000000029543", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -887220, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-1000000000000000000", + "amount1Before": "0", + "amount1Delta": "2", + "executionPrice": "0.0000000000000000020000", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.0000000000000000000000000000000000000029543", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -887220, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-1000000000000000000", + "amount1Before": "0", + "amount1Delta": "2", + "executionPrice": "0.0000000000000000020000", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.0000000000000000000000000000000000000029543", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -887220, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token1 for token0 to price 0.40000 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-36796311322104302059276007071937639893", + "amount1Before": "0", + "amount1Delta": "1268717215714495283", + "executionPrice": "0.000000000000000000034479", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "647583145675012958539816297734564973", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": -9164, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests initialized at the min ratio swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "36796311322104302062438284732106019258", + "amount0Delta": "-36796311322104302061173373668038667492", + "amount1Before": "0", + "amount1Delta": "3171793039286238112", + "executionPrice": "0.000000000000000000086199", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1618957864187523634078592530170978294", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "0.0000000000000000000000000000000000000029390", + "tickAfter": 9163, + "tickBefore": -887272, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1000", + "amount1Before": "2000000000000000000", + "amount1Delta": "-998", + "executionPrice": "0.99800", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-998", + "amount1Before": "2000000000000000000", + "amount1Delta": "1000", + "executionPrice": "1.0020", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1000000000000000000", + "amount1Before": "2000000000000000000", + "amount1Delta": "-666444407401233536", + "executionPrice": "0.66644", + "feeGrowthGlobal0X128Delta": "85070591730234956148210572796405514", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.44459", + "poolPriceBefore": "1.0000", + "tickAfter": -8107, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "828841545518949575", + "amount1Before": "2000000000000000000", + "amount1Delta": "-585786437626904950", + "executionPrice": "0.70675", + "feeGrowthGlobal0X128Delta": "70510040727899606087499539976421836", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-666444407401233536", + "amount1Before": "2000000000000000000", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.5005", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "85070591730234956148210572796405515", + "poolPriceAfter": "2.2493", + "poolPriceBefore": "1.0000", + "tickAfter": 8106, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-585786437626904950", + "amount1Before": "2000000000000000000", + "amount1Delta": "828841545518949574", + "executionPrice": "1.4149", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "70510040727899435946316079507190105", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1002", + "amount1Before": "2000000000000000000", + "amount1Delta": "-1000", + "executionPrice": "0.99800", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "2001000500250125077", + "amount1Before": "2000000000000000000", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.49975", + "feeGrowthGlobal0X128Delta": "170226296608774038574344664756091446", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.25000", + "poolPriceBefore": "1.0000", + "tickAfter": -13864, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "828841545518949575", + "amount1Before": "2000000000000000000", + "amount1Delta": "-585786437626904950", + "executionPrice": "0.70675", + "feeGrowthGlobal0X128Delta": "70510040727899606087499539976421836", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1162859089713235953", + "amount1Before": "2000000000000000000", + "amount1Delta": "-735088935932648266", + "executionPrice": "0.63214", + "feeGrowthGlobal0X128Delta": "98925110860787308007692432636113977", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "2000000000000000000", + "poolBalance1": "2000000000000000000", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-1000", + "amount1Before": "2000000000000000000", + "amount1Delta": "1002", + "executionPrice": "1.0020", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-1000000000000000000", + "amount1Before": "2000000000000000000", + "amount1Delta": "2001000500250125079", + "executionPrice": "2.0010", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170226296608774378856711585694554910", + "poolPriceAfter": "4.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 13863, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-585786437626904950", + "amount1Before": "2000000000000000000", + "amount1Delta": "828841545518949574", + "executionPrice": "1.4149", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "70510040727899435946316079507190105", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "2000000000000000000", + "poolBalance1": "2000000000000000000", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-735088935932648266", + "amount1Before": "2000000000000000000", + "amount1Delta": "1162859089713235954", + "executionPrice": "1.5819", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "98925110860787308007692432636113978", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1000", + "amount1Before": "999700069986003", + "amount1Delta": "-998", + "executionPrice": "0.99800", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-998", + "amount1Before": "999700069986003", + "amount1Delta": "1000", + "executionPrice": "1.0020", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1000700370186095", + "amount1Before": "999700069986003", + "amount1Delta": "-999700069986002", + "executionPrice": "0.99900", + "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "1.0000", + "tickAfter": -887272, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1000700370186095", + "amount1Before": "999700069986003", + "amount1Delta": "-999700069986002", + "executionPrice": "0.99900", + "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-999700069986002", + "amount1Before": "999700069986003", + "amount1Delta": "1000700370186095", + "executionPrice": "1.0010", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.0000", + "tickAfter": 887271, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-999700069986002", + "amount1Before": "999700069986003", + "amount1Delta": "1000700370186095", + "executionPrice": "1.0010", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1002", + "amount1Before": "999700069986003", + "amount1Delta": "-1000", + "executionPrice": "0.99800", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1000700370186095", + "amount1Before": "999700069986003", + "amount1Delta": "-999700069986002", + "executionPrice": "0.99900", + "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "1.0000", + "tickAfter": -887272, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1000700370186095", + "amount1Before": "999700069986003", + "amount1Delta": "-999700069986002", + "executionPrice": "0.99900", + "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "1000700370186095", + "amount1Before": "999700069986003", + "amount1Delta": "-999700069986002", + "executionPrice": "0.99900", + "feeGrowthGlobal0X128Delta": "85130172636557991529041720559172", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "999700069986003", + "poolBalance1": "999700069986003", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-1000", + "amount1Before": "999700069986003", + "amount1Delta": "1002", + "executionPrice": "1.0020", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-999700069986002", + "amount1Before": "999700069986003", + "amount1Delta": "1000700370186095", + "executionPrice": "1.0010", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.0000", + "tickAfter": 887271, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-999700069986002", + "amount1Before": "999700069986003", + "amount1Delta": "1000700370186095", + "executionPrice": "1.0010", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "999700069986003", + "poolBalance1": "999700069986003", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests low fee, large liquidity around current price (stable swap) swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "999700069986003", + "amount0Delta": "-999700069986002", + "amount1Before": "999700069986003", + "amount1Delta": "1000700370186095", + "executionPrice": "1.0010", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "85130172636557991529041720559172", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "1000", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "0", + "executionPrice": "0.0000", + "feeGrowthGlobal0X128Delta": "29575000", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "0", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "1000", + "executionPrice": "-Infinity", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "29575000", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "1000000000000000000", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "-996999999999999318", + "executionPrice": "0.99700", + "feeGrowthGlobal0X128Delta": "88725000000017597125", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "1000000000000000000", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "-996999999999999318", + "executionPrice": "0.99700", + "feeGrowthGlobal0X128Delta": "88725000000017597125", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "-996999999999999232", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.0030", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "88725000000020140575", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "-996999999999999232", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.0030", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "88725000000020140575", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "145660", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "-1000", + "executionPrice": "0.0068653", + "feeGrowthGlobal0X128Delta": "12924275", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "1003009027081361181", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.99700", + "feeGrowthGlobal0X128Delta": "88991975927793784300", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "1003009027081361181", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.99700", + "feeGrowthGlobal0X128Delta": "88991975927793784300", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "6706554036096900675845906992672697", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "-4228872409409224753601131225116702", + "executionPrice": "0.63056", + "feeGrowthGlobal0X128Delta": "595039006852697512464428097924911949", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "11505743598341114571255423385623647", + "poolBalance1": "11505743598341114571255423385506404", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "-1000", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "145660", + "executionPrice": "145.66", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "12924275", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "-1000000000000000000", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "1003009027081361094", + "executionPrice": "1.0030", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "88991975927793784300", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "-1000000000000000000", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "1003009027081361094", + "executionPrice": "1.0030", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "88991975927793784300", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "11505743598341114571255423385623647", + "poolBalance1": "11505743598341114571255423385506404", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests max full range liquidity at 1:1 price with default fee swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "11505743598341114571255423385623647", + "amount0Delta": "-4228872409409224753601131224936259", + "amount1Before": "11505743598341114571255423385506404", + "amount1Delta": "6706554036096900675845906992220230", + "executionPrice": "1.5859", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "595039006852697512464428097884749099", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "1000", + "amount1Before": "1994009290088178439", + "amount1Delta": "-991", + "executionPrice": "0.99100", + "feeGrowthGlobal0X128Delta": "510423550381407695195", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.99402", + "poolPriceBefore": "1.0000", + "tickAfter": -61, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-991", + "amount1Before": "1994009290088178439", + "amount1Delta": "1000", + "executionPrice": "1.0091", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "1.0060", + "poolPriceBefore": "1.0000", + "tickAfter": 60, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "1000000000000000000", + "amount1Before": "1994009290088178439", + "amount1Delta": "-662011820624678025", + "executionPrice": "0.66201", + "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.44355", + "poolPriceBefore": "1.0000", + "tickAfter": -8130, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "824893095908431542", + "amount1Before": "1994009290088178439", + "amount1Delta": "-579795727715083389", + "executionPrice": "0.70287", + "feeGrowthGlobal0X128Delta": "421044862698692740725170743495410672", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-662011820624678025", + "amount1Before": "1994009290088178439", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.5105", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", + "poolPriceAfter": "2.2545", + "poolPriceBefore": "1.0000", + "tickAfter": 8129, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-579795727715083389", + "amount1Before": "1994009290088178439", + "amount1Delta": "824893095908431542", + "executionPrice": "1.4227", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "421044862698692740725170743495410672", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "1011", + "amount1Before": "1994009290088178439", + "amount1Delta": "-1000", + "executionPrice": "0.98912", + "feeGrowthGlobal0X128Delta": "680564733841876926926", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.99402", + "poolPriceBefore": "1.0000", + "tickAfter": -61, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "2024171064311638316", + "amount1Before": "1994009290088178439", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.49403", + "feeGrowthGlobal0X128Delta": "1033184581225259164735720748018047287", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.24701", + "poolPriceBefore": "1.0000", + "tickAfter": -13984, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "824893095908431542", + "amount1Before": "1994009290088178439", + "amount1Delta": "-579795727715083389", + "executionPrice": "0.70287", + "feeGrowthGlobal0X128Delta": "421044862698692740725170743495410672", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "1159748196632793863", + "amount1Before": "1994009290088178439", + "amount1Delta": "-729098226020826705", + "executionPrice": "0.62867", + "feeGrowthGlobal0X128Delta": "591962792073745646583043420635066071", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "1994009290088178439", + "poolBalance1": "1994009290088178439", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-1000", + "amount1Before": "1994009290088178439", + "amount1Delta": "1011", + "executionPrice": "1.0110", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "680564733841876926926", + "poolPriceAfter": "1.0060", + "poolPriceBefore": "1.0000", + "tickAfter": 60, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-1000000000000000000", + "amount1Before": "1994009290088178439", + "amount1Delta": "2024171064311638316", + "executionPrice": "2.0242", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1033184581225259164735720748018047287", + "poolPriceAfter": "4.0484", + "poolPriceBefore": "1.0000", + "tickAfter": 13983, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-579795727715083389", + "amount1Before": "1994009290088178439", + "amount1Delta": "824893095908431542", + "executionPrice": "1.4227", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "421044862698692740725170743495410672", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "1994009290088178439", + "poolBalance1": "1994009290088178439", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 0 liquidity, all liquidity around current price swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "1994009290088178439", + "amount0Delta": "-729098226020826705", + "amount1Before": "1994009290088178439", + "amount1Delta": "1159748196632793863", + "executionPrice": "1.5907", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "591962792073745646583043420635066071", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1000", + "amount1Before": "2000000000000000000", + "amount1Delta": "-996", + "executionPrice": "0.99600", + "feeGrowthGlobal0X128Delta": "510423550381407695195", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-996", + "amount1Before": "2000000000000000000", + "amount1Delta": "1000", + "executionPrice": "1.0040", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1000000000000000000", + "amount1Before": "2000000000000000000", + "amount1Delta": "-665331998665331998", + "executionPrice": "0.66533", + "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.44533", + "poolPriceBefore": "1.0000", + "tickAfter": -8090, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "830919884399388263", + "amount1Before": "2000000000000000000", + "amount1Delta": "-585786437626904951", + "executionPrice": "0.70499", + "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-665331998665331998", + "amount1Before": "2000000000000000000", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.5030", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", + "poolPriceAfter": "2.2455", + "poolPriceBefore": "1.0000", + "tickAfter": 8089, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-585786437626904951", + "amount1Before": "2000000000000000000", + "amount1Delta": "830919884399388263", + "executionPrice": "1.4185", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1005", + "amount1Before": "2000000000000000000", + "amount1Delta": "-1000", + "executionPrice": "0.99502", + "feeGrowthGlobal0X128Delta": "680564733841876926926", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "2006018054162487463", + "amount1Before": "2000000000000000000", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.49850", + "feeGrowthGlobal0X128Delta": "1023918857334819954209013958517557896", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.25000", + "poolPriceBefore": "1.0000", + "tickAfter": -13864, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "830919884399388263", + "amount1Before": "2000000000000000000", + "amount1Delta": "-585786437626904951", + "executionPrice": "0.70499", + "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "1165774985123750584", + "amount1Before": "2000000000000000000", + "amount1Delta": "-735088935932648267", + "executionPrice": "0.63056", + "feeGrowthGlobal0X128Delta": "595039006852697554786973994761078087", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "2000000000000000000", + "poolBalance1": "2000000000000000000", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-1000", + "amount1Before": "2000000000000000000", + "amount1Delta": "1005", + "executionPrice": "1.0050", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "680564733841876926926", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-1000000000000000000", + "amount1Before": "2000000000000000000", + "amount1Delta": "2006018054162487463", + "executionPrice": "2.0060", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1023918857334819954209013958517557896", + "poolPriceAfter": "4.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 13863, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-585786437626904951", + "amount1Before": "2000000000000000000", + "amount1Delta": "830919884399388263", + "executionPrice": "1.4185", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "2000000000000000000", + "poolBalance1": "2000000000000000000", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "2000000000000000000", + "amount0Delta": "-735088935932648267", + "amount1Before": "2000000000000000000", + "amount1Delta": "1165774985123750584", + "executionPrice": "1.5859", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "595039006852697554786973994761078087", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "1000", + "amount1Before": "3994009290088178439", + "amount1Delta": "-996", + "executionPrice": "0.99600", + "feeGrowthGlobal0X128Delta": "510423550381407695195", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-996", + "amount1Before": "3994009290088178439", + "amount1Delta": "1000", + "executionPrice": "1.0040", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "1000000000000000000", + "amount1Before": "3994009290088178439", + "amount1Delta": "-795933705287758544", + "executionPrice": "0.79593", + "feeGrowthGlobal0X128Delta": "256749882580179971840679703106063897", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.63923", + "poolPriceBefore": "1.0000", + "tickAfter": -4476, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "1000000000000000000", + "amount1Before": "3994009290088178439", + "amount1Delta": "-795933705287758544", + "executionPrice": "0.79593", + "feeGrowthGlobal0X128Delta": "256749882580179971840679703106063897", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.63923", + "poolPriceBefore": "1.0000", + "tickAfter": -4476, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-795933705287758544", + "amount1Before": "3994009290088178439", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.2564", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "256749882580179971840679703106063897", + "poolPriceAfter": "1.5644", + "poolPriceBefore": "1.0000", + "tickAfter": 4475, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-795933705287758544", + "amount1Before": "3994009290088178439", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.2564", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "256749882580179971840679703106063897", + "poolPriceAfter": "1.5644", + "poolPriceBefore": "1.0000", + "tickAfter": 4475, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "1005", + "amount1Before": "3994009290088178439", + "amount1Delta": "-1000", + "executionPrice": "0.99502", + "feeGrowthGlobal0X128Delta": "680564733841876926926", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "1342022152495072924", + "amount1Before": "3994009290088178439", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.74514", + "feeGrowthGlobal0X128Delta": "344037963272993171369654596359692757", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.56026", + "poolPriceBefore": "1.0000", + "tickAfter": -5794, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "1342022152495072924", + "amount1Before": "3994009290088178439", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.74514", + "feeGrowthGlobal0X128Delta": "344037963272993171369654596359692757", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.56026", + "poolPriceBefore": "1.0000", + "tickAfter": -5794, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "2325523181756544449", + "amount1Before": "3994009290088178439", + "amount1Delta": "-1464187161953474971", + "executionPrice": "0.62962", + "feeGrowthGlobal0X128Delta": "595039006852697724928157455230309818", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "3994009290088178439", + "poolBalance1": "3994009290088178439", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-1000", + "amount1Before": "3994009290088178439", + "amount1Delta": "1005", + "executionPrice": "1.0050", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "680564733841876926926", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-1000000000000000000", + "amount1Before": "3994009290088178439", + "amount1Delta": "1342022152495072924", + "executionPrice": "1.3420", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "344037963272993171369654596359692757", + "poolPriceAfter": "1.7849", + "poolPriceBefore": "1.0000", + "tickAfter": 5793, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-1000000000000000000", + "amount1Before": "3994009290088178439", + "amount1Delta": "1342022152495072924", + "executionPrice": "1.3420", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "344037963272993171369654596359692757", + "poolPriceAfter": "1.7849", + "poolPriceBefore": "1.0000", + "tickAfter": 5793, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "3994009290088178439", + "poolBalance1": "3994009290088178439", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:1 price, additional liquidity around current price swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "3994009290088178439", + "amount0Delta": "-1464187161953474971", + "amount1Before": "3994009290088178439", + "amount1Delta": "2325523181756544449", + "executionPrice": "1.5883", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "595039006852697724928157455230309818", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "1000", + "amount1Before": "632455532033675867", + "amount1Delta": "-99", + "executionPrice": "0.099000", + "feeGrowthGlobal0X128Delta": "510423550381407695195", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.10000", + "poolPriceBefore": "0.10000", + "tickAfter": -23028, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-9969", + "amount1Before": "632455532033675867", + "amount1Delta": "1000", + "executionPrice": "0.10031", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "0.10000", + "poolPriceBefore": "0.10000", + "tickAfter": -23028, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "1000000000000000000", + "amount1Before": "632455532033675867", + "amount1Delta": "-86123526743846551", + "executionPrice": "0.086124", + "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.074620", + "poolPriceBefore": "0.10000", + "tickAfter": -25955, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "poolBalance0": "6324555320336758664", + "poolBalance1": "632455532033675867", + "poolPriceBefore": "0.10000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-3869747612262812753", + "amount1Before": "632455532033675867", + "amount1Delta": "1000000000000000000", + "executionPrice": "0.25841", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407865336245371616884047", + "poolPriceAfter": "0.66378", + "poolPriceBefore": "0.10000", + "tickAfter": -4099, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-3869747612262812753", + "amount1Before": "632455532033675867", + "amount1Delta": "1000000000000000000", + "executionPrice": "0.25841", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407865336245371616884047", + "poolPriceAfter": "0.66378", + "poolPriceBefore": "0.10000", + "tickAfter": -4099, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "10032", + "amount1Before": "632455532033675867", + "amount1Delta": "-1000", + "executionPrice": "0.099681", + "feeGrowthGlobal0X128Delta": "5274376687274546183682", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.10000", + "poolPriceBefore": "0.10000", + "tickAfter": -23028, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "36907032419362389223785084665766560335", + "amount1Before": "632455532033675867", + "amount1Delta": "-632455532033675838", + "executionPrice": "0.000000000000000000017136", + "feeGrowthGlobal0X128Delta": "18838218521532665615644565874197034349094564536667752274", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "0.10000", + "tickAfter": -887272, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "poolBalance0": "6324555320336758664", + "poolBalance1": "632455532033675867", + "poolPriceBefore": "0.10000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` +Object { + "poolBalance0": "6324555320336758664", + "poolBalance1": "632455532033675867", + "poolPriceBefore": "0.10000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "6324555320336758664", + "poolBalance1": "632455532033675867", + "poolPriceBefore": "0.10000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-1000", + "amount1Before": "632455532033675867", + "amount1Delta": "102", + "executionPrice": "0.10200", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "170141183460469231731", + "poolPriceAfter": "0.10000", + "poolPriceBefore": "0.10000", + "tickAfter": -23028, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-1000000000000000000", + "amount1Before": "632455532033675867", + "amount1Delta": "119138326055954425", + "executionPrice": "0.11914", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "60811007371978153949466126675899993", + "poolPriceAfter": "0.14109", + "poolPriceBefore": "0.10000", + "tickAfter": -19585, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-1000000000000000000", + "amount1Before": "632455532033675867", + "amount1Delta": "119138326055954425", + "executionPrice": "0.11914", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "60811007371978153949466126675899993", + "poolPriceAfter": "0.14109", + "poolPriceBefore": "0.10000", + "tickAfter": -19585, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-3162277660168379331", + "amount1Before": "632455532033675867", + "amount1Delta": "634358607857247611", + "executionPrice": "0.20060", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "323791572837503501799197590655727195", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "0.10000", + "tickAfter": -9164, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 1:10 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "6324555320336758664", + "amount0Delta": "-5059644256269406930", + "amount1Before": "632455532033675867", + "amount1Delta": "2537434431428990440", + "executionPrice": "0.50150", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1295166291350014177337973823092140516", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "0.10000", + "tickAfter": 9163, + "tickBefore": -23028, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "1000", + "amount1Before": "6324555320336758664", + "amount1Delta": "-9969", + "executionPrice": "9.9690", + "feeGrowthGlobal0X128Delta": "510423550381407695195", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "10.000", + "poolPriceBefore": "10.000", + "tickAfter": 23027, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "-99", + "amount1Before": "6324555320336758664", + "amount1Delta": "1000", + "executionPrice": "10.101", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "10.000", + "poolPriceBefore": "10.000", + "tickAfter": 23027, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "1000000000000000000", + "amount1Before": "6324555320336758664", + "amount1Delta": "-3869747612262812754", + "executionPrice": "3.8697", + "feeGrowthGlobal0X128Delta": "510423550381407865336245371616884048", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.5065", + "poolPriceBefore": "10.000", + "tickAfter": 4098, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "1000000000000000000", + "amount1Before": "6324555320336758664", + "amount1Delta": "-3869747612262812754", + "executionPrice": "3.8697", + "feeGrowthGlobal0X128Delta": "510423550381407865336245371616884048", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.5065", + "poolPriceBefore": "10.000", + "tickAfter": 4098, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "-86123526743846551", + "amount1Before": "6324555320336758664", + "amount1Delta": "1000000000000000000", + "executionPrice": "11.611", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", + "poolPriceAfter": "13.401", + "poolPriceBefore": "10.000", + "tickAfter": 25954, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "poolBalance0": "632455532033675867", + "poolBalance1": "6324555320336758664", + "poolPriceBefore": "10.000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "102", + "amount1Before": "6324555320336758664", + "amount1Delta": "-1000", + "executionPrice": "9.8039", + "feeGrowthGlobal0X128Delta": "170141183460469231731", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "10.000", + "poolPriceBefore": "10.000", + "tickAfter": 23027, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "119138326055954425", + "amount1Before": "6324555320336758664", + "amount1Delta": "-1000000000000000000", + "executionPrice": "8.3936", + "feeGrowthGlobal0X128Delta": "60811007371978153949466126675899993", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "7.0877", + "poolPriceBefore": "10.000", + "tickAfter": 19584, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "119138326055954425", + "amount1Before": "6324555320336758664", + "amount1Delta": "-1000000000000000000", + "executionPrice": "8.3936", + "feeGrowthGlobal0X128Delta": "60811007371978153949466126675899993", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "7.0877", + "poolPriceBefore": "10.000", + "tickAfter": 19584, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "2537434431428990438", + "amount1Before": "6324555320336758664", + "amount1Delta": "-5059644256269406930", + "executionPrice": "1.9940", + "feeGrowthGlobal0X128Delta": "1295166291350014007196790362622908786", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "10.000", + "tickAfter": -9164, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token0 for token1 to price 2.5000 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "634358607857247610", + "amount1Before": "6324555320336758664", + "amount1Delta": "-3162277660168379331", + "executionPrice": "4.9850", + "feeGrowthGlobal0X128Delta": "323791572837503501799197590655727196", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "10.000", + "tickAfter": 9163, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "-1000", + "amount1Before": "6324555320336758664", + "amount1Delta": "10032", + "executionPrice": "10.032", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "5274376687274546183682", + "poolPriceAfter": "10.000", + "poolPriceBefore": "10.000", + "tickAfter": 23027, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "632455532033675867", + "amount0Delta": "-632455532033675838", + "amount1Before": "6324555320336758664", + "amount1Delta": "36907032426281581270030941278837275671", + "executionPrice": "5.8355e+19", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "18838218525064384185660173270402201838945341643205005201", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "10.000", + "tickAfter": 887271, + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "poolBalance0": "632455532033675867", + "poolBalance1": "6324555320336758664", + "poolPriceBefore": "10.000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "632455532033675867", + "poolBalance1": "6324555320336758664", + "poolPriceBefore": "10.000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, 10:1 price, 2e18 max range liquidity swap token1 for token0 to price 2.5000 1`] = ` +Object { + "poolBalance0": "632455532033675867", + "poolBalance1": "6324555320336758664", + "poolPriceBefore": "10.000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 23027, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "1.0000", + "tickAfter": -887272, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-996", + "amount1Before": "0", + "amount1Delta": "1000", + "executionPrice": "1.0040", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "1.0000", + "tickAfter": -887272, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-665331998665331998", + "amount1Before": "0", + "amount1Delta": "1000000000000000000", + "executionPrice": "1.5030", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "510423550381407695195061911147652317", + "poolPriceAfter": "2.2455", + "poolPriceBefore": "1.0000", + "tickAfter": 8089, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-585786437626904951", + "amount1Before": "0", + "amount1Delta": "830919884399388263", + "executionPrice": "1.4185", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "1.0000", + "tickAfter": -887272, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.0000000000000000000000000000000000000029390", + "poolPriceBefore": "1.0000", + "tickAfter": -887272, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "0", + "amount1Before": "0", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "1995041008271423675", + "poolBalance1": "0", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-1000", + "amount1Before": "0", + "amount1Delta": "1005", + "executionPrice": "1.0050", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "680564733841876926926", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 0, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-1000000000000000000", + "amount1Before": "0", + "amount1Delta": "2006018054162487463", + "executionPrice": "2.0060", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "1023918857334819954209013958517557896", + "poolPriceAfter": "4.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 13863, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-585786437626904951", + "amount1Before": "0", + "amount1Delta": "830919884399388263", + "executionPrice": "1.4185", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "424121077477644648929101317621422688", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "1995041008271423675", + "poolBalance1": "0", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token0 liquidity only swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "1995041008271423675", + "amount0Delta": "-735088935932648267", + "amount1Before": "0", + "amount1Delta": "1165774985123750584", + "executionPrice": "1.5859", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "595039006852697554786973994761078087", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap exactly 0.0000000000000010000 token0 for token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1000", + "amount1Before": "1995041008271423675", + "amount1Delta": "-996", + "executionPrice": "0.99600", + "feeGrowthGlobal0X128Delta": "510423550381407695195", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap exactly 0.0000000000000010000 token1 for token0 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.0000", + "tickAfter": 887271, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token0 for token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1000000000000000000", + "amount1Before": "1995041008271423675", + "amount1Delta": "-665331998665331998", + "executionPrice": "0.66533", + "feeGrowthGlobal0X128Delta": "510423550381407695195061911147652317", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.44533", + "poolPriceBefore": "1.0000", + "tickAfter": -8090, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token0 for token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "830919884399388263", + "amount1Before": "1995041008271423675", + "amount1Delta": "-585786437626904951", + "executionPrice": "0.70499", + "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token1 for token0 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.0000", + "tickAfter": 887271, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap exactly 1.0000 token1 for token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token0 for exactly 0.0000000000000010000 token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1005", + "amount1Before": "1995041008271423675", + "amount1Delta": "-1000", + "executionPrice": "0.99502", + "feeGrowthGlobal0X128Delta": "680564733841876926926", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "1.0000", + "poolPriceBefore": "1.0000", + "tickAfter": -1, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token0 for exactly 1.0000 token1 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "2006018054162487463", + "amount1Before": "1995041008271423675", + "amount1Delta": "-1000000000000000000", + "executionPrice": "0.49850", + "feeGrowthGlobal0X128Delta": "1023918857334819954209013958517557896", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.25000", + "poolPriceBefore": "1.0000", + "tickAfter": -13864, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token0 for exactly 1.0000 token1 to price 0.50000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "830919884399388263", + "amount1Before": "1995041008271423675", + "amount1Delta": "-585786437626904951", + "executionPrice": "0.70499", + "feeGrowthGlobal0X128Delta": "424121077477644648929101317621422688", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.50000", + "poolPriceBefore": "1.0000", + "tickAfter": -6932, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token0 for token1 to price 0.40000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "1165774985123750584", + "amount1Before": "1995041008271423675", + "amount1Delta": "-735088935932648267", + "executionPrice": "0.63056", + "feeGrowthGlobal0X128Delta": "595039006852697554786973994761078087", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "0.40000", + "poolPriceBefore": "1.0000", + "tickAfter": -9164, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token0 for token1 to price 2.5000 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "1995041008271423675", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token1 for exactly 0.0000000000000010000 token0 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.0000", + "tickAfter": 887271, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token1 for exactly 1.0000 token0 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "3.4026e+38", + "poolPriceBefore": "1.0000", + "tickAfter": 887271, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token1 for exactly 1.0000 token0 to price 2.0000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "2.0000", + "poolPriceBefore": "1.0000", + "tickAfter": 6931, + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token1 for token0 to price 0.40000 1`] = ` +Object { + "poolBalance0": "0", + "poolBalance1": "1995041008271423675", + "poolPriceBefore": "1.0000", + "swapError": "VM Exception while processing transaction: revert SPL", + "tickBefore": 0, +} +`; + +exports[`UniswapV3Pool swap tests medium fee, token1 liquidity only swap token1 for token0 to price 2.5000 1`] = ` +Object { + "amount0Before": "0", + "amount0Delta": "0", + "amount1Before": "1995041008271423675", + "amount1Delta": "0", + "executionPrice": "NaN", + "feeGrowthGlobal0X128Delta": "0", + "feeGrowthGlobal1X128Delta": "0", + "poolPriceAfter": "2.5000", + "poolPriceBefore": "1.0000", + "tickAfter": 9163, + "tickBefore": 0, +} +`; diff --git a/lib/uniswap/v3-core/test/shared/checkObservationEquals.ts b/lib/uniswap/v3-core/test/shared/checkObservationEquals.ts new file mode 100644 index 0000000..744116c --- /dev/null +++ b/lib/uniswap/v3-core/test/shared/checkObservationEquals.ts @@ -0,0 +1,38 @@ +import { BigNumber, BigNumberish } from 'ethers' +import { expect } from './expect' + +// helper function because we cannot do a simple deep equals with the +// observation result object returned from ethers because it extends array +export default function checkObservationEquals( + { + tickCumulative, + blockTimestamp, + initialized, + secondsPerLiquidityCumulativeX128, + }: { + tickCumulative: BigNumber + secondsPerLiquidityCumulativeX128: BigNumber + initialized: boolean + blockTimestamp: number + }, + expected: { + tickCumulative: BigNumberish + secondsPerLiquidityCumulativeX128: BigNumberish + initialized: boolean + blockTimestamp: number + } +) { + expect( + { + initialized, + blockTimestamp, + tickCumulative: tickCumulative.toString(), + secondsPerLiquidityCumulativeX128: secondsPerLiquidityCumulativeX128.toString(), + }, + `observation is equivalent` + ).to.deep.eq({ + ...expected, + tickCumulative: expected.tickCumulative.toString(), + secondsPerLiquidityCumulativeX128: expected.secondsPerLiquidityCumulativeX128.toString(), + }) +} diff --git a/lib/uniswap/v3-core/test/shared/expect.ts b/lib/uniswap/v3-core/test/shared/expect.ts new file mode 100644 index 0000000..ac8f19b --- /dev/null +++ b/lib/uniswap/v3-core/test/shared/expect.ts @@ -0,0 +1,8 @@ +import { expect, use } from 'chai' +import { solidity } from 'ethereum-waffle' +import { jestSnapshotPlugin } from 'mocha-chai-jest-snapshot' + +use(solidity) +use(jestSnapshotPlugin()) + +export { expect } diff --git a/lib/uniswap/v3-core/test/shared/fixtures.ts b/lib/uniswap/v3-core/test/shared/fixtures.ts new file mode 100644 index 0000000..5a768a6 --- /dev/null +++ b/lib/uniswap/v3-core/test/shared/fixtures.ts @@ -0,0 +1,93 @@ +import { BigNumber } from 'ethers' +import { ethers } from 'hardhat' +import { MockTimeUniswapV3Pool } from '../../typechain/MockTimeUniswapV3Pool' +import { TestERC20 } from '../../typechain/TestERC20' +import { UniswapV3Factory } from '../../typechain/UniswapV3Factory' +import { TestUniswapV3Callee } from '../../typechain/TestUniswapV3Callee' +import { TestUniswapV3Router } from '../../typechain/TestUniswapV3Router' +import { MockTimeUniswapV3PoolDeployer } from '../../typechain/MockTimeUniswapV3PoolDeployer' + +import { Fixture } from 'ethereum-waffle' + +interface FactoryFixture { + factory: UniswapV3Factory +} + +async function factoryFixture(): Promise { + const factoryFactory = await ethers.getContractFactory('UniswapV3Factory') + const factory = (await factoryFactory.deploy()) as UniswapV3Factory + return { factory } +} + +interface TokensFixture { + token0: TestERC20 + token1: TestERC20 + token2: TestERC20 +} + +async function tokensFixture(): Promise { + const tokenFactory = await ethers.getContractFactory('TestERC20') + const tokenA = (await tokenFactory.deploy(BigNumber.from(2).pow(255))) as TestERC20 + const tokenB = (await tokenFactory.deploy(BigNumber.from(2).pow(255))) as TestERC20 + const tokenC = (await tokenFactory.deploy(BigNumber.from(2).pow(255))) as TestERC20 + + const [token0, token1, token2] = [tokenA, tokenB, tokenC].sort((tokenA, tokenB) => + tokenA.address.toLowerCase() < tokenB.address.toLowerCase() ? -1 : 1 + ) + + return { token0, token1, token2 } +} + +type TokensAndFactoryFixture = FactoryFixture & TokensFixture + +interface PoolFixture extends TokensAndFactoryFixture { + swapTargetCallee: TestUniswapV3Callee + swapTargetRouter: TestUniswapV3Router + createPool( + fee: number, + tickSpacing: number, + firstToken?: TestERC20, + secondToken?: TestERC20 + ): Promise +} + +// Monday, October 5, 2020 9:00:00 AM GMT-05:00 +export const TEST_POOL_START_TIME = 1601906400 + +export const poolFixture: Fixture = async function (): Promise { + const { factory } = await factoryFixture() + const { token0, token1, token2 } = await tokensFixture() + + const MockTimeUniswapV3PoolDeployerFactory = await ethers.getContractFactory('MockTimeUniswapV3PoolDeployer') + const MockTimeUniswapV3PoolFactory = await ethers.getContractFactory('MockTimeUniswapV3Pool') + + const calleeContractFactory = await ethers.getContractFactory('TestUniswapV3Callee') + const routerContractFactory = await ethers.getContractFactory('TestUniswapV3Router') + + const swapTargetCallee = (await calleeContractFactory.deploy()) as TestUniswapV3Callee + const swapTargetRouter = (await routerContractFactory.deploy()) as TestUniswapV3Router + + return { + token0, + token1, + token2, + factory, + swapTargetCallee, + swapTargetRouter, + createPool: async (fee, tickSpacing, firstToken = token0, secondToken = token1) => { + const mockTimePoolDeployer = + (await MockTimeUniswapV3PoolDeployerFactory.deploy()) as MockTimeUniswapV3PoolDeployer + const tx = await mockTimePoolDeployer.deploy( + factory.address, + firstToken.address, + secondToken.address, + fee, + tickSpacing + ) + + const receipt = await tx.wait() + const poolAddress = receipt.events?.[0].args?.pool as string + return MockTimeUniswapV3PoolFactory.attach(poolAddress) as MockTimeUniswapV3Pool + }, + } +} diff --git a/lib/uniswap/v3-core/test/shared/format.ts b/lib/uniswap/v3-core/test/shared/format.ts new file mode 100644 index 0000000..8d23da5 --- /dev/null +++ b/lib/uniswap/v3-core/test/shared/format.ts @@ -0,0 +1,10 @@ +import { Decimal } from 'decimal.js' +import { BigNumberish } from 'ethers' + +export function formatTokenAmount(num: BigNumberish): string { + return new Decimal(num.toString()).dividedBy(new Decimal(10).pow(18)).toPrecision(5) +} + +export function formatPrice(price: BigNumberish): string { + return new Decimal(price.toString()).dividedBy(new Decimal(2).pow(96)).pow(2).toPrecision(5) +} diff --git a/lib/uniswap/v3-core/test/shared/snapshotGasCost.ts b/lib/uniswap/v3-core/test/shared/snapshotGasCost.ts new file mode 100644 index 0000000..5a606d2 --- /dev/null +++ b/lib/uniswap/v3-core/test/shared/snapshotGasCost.ts @@ -0,0 +1,27 @@ +import { TransactionReceipt, TransactionResponse } from '@ethersproject/abstract-provider' +import { expect } from './expect' +import { Contract, BigNumber, ContractTransaction } from 'ethers' + +export default async function snapshotGasCost( + x: + | TransactionResponse + | Promise + | ContractTransaction + | Promise + | TransactionReceipt + | Promise + | BigNumber + | Contract + | Promise +): Promise { + const resolved = await x + if ('deployTransaction' in resolved) { + const receipt = await resolved.deployTransaction.wait() + expect(receipt.gasUsed.toNumber()).toMatchSnapshot() + } else if ('wait' in resolved) { + const waited = await resolved.wait() + expect(waited.gasUsed.toNumber()).toMatchSnapshot() + } else if (BigNumber.isBigNumber(resolved)) { + expect(resolved.toNumber()).toMatchSnapshot() + } +} diff --git a/lib/uniswap/v3-core/test/shared/utilities.ts b/lib/uniswap/v3-core/test/shared/utilities.ts new file mode 100644 index 0000000..098149f --- /dev/null +++ b/lib/uniswap/v3-core/test/shared/utilities.ts @@ -0,0 +1,258 @@ +import bn from 'bignumber.js' +import { BigNumber, BigNumberish, constants, Contract, ContractTransaction, utils, Wallet } from 'ethers' +import { TestUniswapV3Callee } from '../../typechain/TestUniswapV3Callee' +import { TestUniswapV3Router } from '../../typechain/TestUniswapV3Router' +import { MockTimeUniswapV3Pool } from '../../typechain/MockTimeUniswapV3Pool' +import { TestERC20 } from '../../typechain/TestERC20' + +export const MaxUint128 = BigNumber.from(2).pow(128).sub(1) + +export const getMinTick = (tickSpacing: number) => Math.ceil(-887272 / tickSpacing) * tickSpacing +export const getMaxTick = (tickSpacing: number) => Math.floor(887272 / tickSpacing) * tickSpacing +export const getMaxLiquidityPerTick = (tickSpacing: number) => + BigNumber.from(2) + .pow(128) + .sub(1) + .div((getMaxTick(tickSpacing) - getMinTick(tickSpacing)) / tickSpacing + 1) + +export const MIN_SQRT_RATIO = BigNumber.from('4295128739') +export const MAX_SQRT_RATIO = BigNumber.from('1461446703485210103287273052203988822378723970342') + +export enum FeeAmount { + LOW = 500, + MEDIUM = 3000, + HIGH = 10000, +} + +export const TICK_SPACINGS: { [amount in FeeAmount]: number } = { + [FeeAmount.LOW]: 10, + [FeeAmount.MEDIUM]: 60, + [FeeAmount.HIGH]: 200, +} + +export function expandTo18Decimals(n: number): BigNumber { + return BigNumber.from(n).mul(BigNumber.from(10).pow(18)) +} + +export function getCreate2Address( + factoryAddress: string, + [tokenA, tokenB]: [string, string], + fee: number, + bytecode: string +): string { + const [token0, token1] = tokenA.toLowerCase() < tokenB.toLowerCase() ? [tokenA, tokenB] : [tokenB, tokenA] + const constructorArgumentsEncoded = utils.defaultAbiCoder.encode( + ['address', 'address', 'uint24'], + [token0, token1, fee] + ) + const create2Inputs = [ + '0xff', + factoryAddress, + // salt + utils.keccak256(constructorArgumentsEncoded), + // init code. bytecode + constructor arguments + utils.keccak256(bytecode), + ] + const sanitizedInputs = `0x${create2Inputs.map((i) => i.slice(2)).join('')}` + return utils.getAddress(`0x${utils.keccak256(sanitizedInputs).slice(-40)}`) +} + +bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 }) + +// returns the sqrt price as a 64x96 +export function encodePriceSqrt(reserve1: BigNumberish, reserve0: BigNumberish): BigNumber { + return BigNumber.from( + new bn(reserve1.toString()) + .div(reserve0.toString()) + .sqrt() + .multipliedBy(new bn(2).pow(96)) + .integerValue(3) + .toString() + ) +} + +export function getPositionKey(address: string, lowerTick: number, upperTick: number): string { + return utils.keccak256(utils.solidityPack(['address', 'int24', 'int24'], [address, lowerTick, upperTick])) +} + +export type SwapFunction = ( + amount: BigNumberish, + to: Wallet | string, + sqrtPriceLimitX96?: BigNumberish +) => Promise +export type SwapToPriceFunction = (sqrtPriceX96: BigNumberish, to: Wallet | string) => Promise +export type FlashFunction = ( + amount0: BigNumberish, + amount1: BigNumberish, + to: Wallet | string, + pay0?: BigNumberish, + pay1?: BigNumberish +) => Promise +export type MintFunction = ( + recipient: string, + tickLower: BigNumberish, + tickUpper: BigNumberish, + liquidity: BigNumberish +) => Promise +export interface PoolFunctions { + swapToLowerPrice: SwapToPriceFunction + swapToHigherPrice: SwapToPriceFunction + swapExact0For1: SwapFunction + swap0ForExact1: SwapFunction + swapExact1For0: SwapFunction + swap1ForExact0: SwapFunction + flash: FlashFunction + mint: MintFunction +} +export function createPoolFunctions({ + swapTarget, + token0, + token1, + pool, +}: { + swapTarget: TestUniswapV3Callee + token0: TestERC20 + token1: TestERC20 + pool: MockTimeUniswapV3Pool +}): PoolFunctions { + async function swapToSqrtPrice( + inputToken: Contract, + targetPrice: BigNumberish, + to: Wallet | string + ): Promise { + const method = inputToken === token0 ? swapTarget.swapToLowerSqrtPrice : swapTarget.swapToHigherSqrtPrice + + await inputToken.approve(swapTarget.address, constants.MaxUint256) + + const toAddress = typeof to === 'string' ? to : to.address + + return method(pool.address, targetPrice, toAddress) + } + + async function swap( + inputToken: Contract, + [amountIn, amountOut]: [BigNumberish, BigNumberish], + to: Wallet | string, + sqrtPriceLimitX96?: BigNumberish + ): Promise { + const exactInput = amountOut === 0 + + const method = + inputToken === token0 + ? exactInput + ? swapTarget.swapExact0For1 + : swapTarget.swap0ForExact1 + : exactInput + ? swapTarget.swapExact1For0 + : swapTarget.swap1ForExact0 + + if (typeof sqrtPriceLimitX96 === 'undefined') { + if (inputToken === token0) { + sqrtPriceLimitX96 = MIN_SQRT_RATIO.add(1) + } else { + sqrtPriceLimitX96 = MAX_SQRT_RATIO.sub(1) + } + } + await inputToken.approve(swapTarget.address, constants.MaxUint256) + + const toAddress = typeof to === 'string' ? to : to.address + + return method(pool.address, exactInput ? amountIn : amountOut, toAddress, sqrtPriceLimitX96) + } + + const swapToLowerPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { + return swapToSqrtPrice(token0, sqrtPriceX96, to) + } + + const swapToHigherPrice: SwapToPriceFunction = (sqrtPriceX96, to) => { + return swapToSqrtPrice(token1, sqrtPriceX96, to) + } + + const swapExact0For1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + return swap(token0, [amount, 0], to, sqrtPriceLimitX96) + } + + const swap0ForExact1: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + return swap(token0, [0, amount], to, sqrtPriceLimitX96) + } + + const swapExact1For0: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + return swap(token1, [amount, 0], to, sqrtPriceLimitX96) + } + + const swap1ForExact0: SwapFunction = (amount, to, sqrtPriceLimitX96) => { + return swap(token1, [0, amount], to, sqrtPriceLimitX96) + } + + const mint: MintFunction = async (recipient, tickLower, tickUpper, liquidity) => { + await token0.approve(swapTarget.address, constants.MaxUint256) + await token1.approve(swapTarget.address, constants.MaxUint256) + return swapTarget.mint(pool.address, recipient, tickLower, tickUpper, liquidity) + } + + const flash: FlashFunction = async (amount0, amount1, to, pay0?: BigNumberish, pay1?: BigNumberish) => { + const fee = await pool.fee() + if (typeof pay0 === 'undefined') { + pay0 = BigNumber.from(amount0) + .mul(fee) + .add(1e6 - 1) + .div(1e6) + .add(amount0) + } + if (typeof pay1 === 'undefined') { + pay1 = BigNumber.from(amount1) + .mul(fee) + .add(1e6 - 1) + .div(1e6) + .add(amount1) + } + return swapTarget.flash(pool.address, typeof to === 'string' ? to : to.address, amount0, amount1, pay0, pay1) + } + + return { + swapToLowerPrice, + swapToHigherPrice, + swapExact0For1, + swap0ForExact1, + swapExact1For0, + swap1ForExact0, + mint, + flash, + } +} + +export interface MultiPoolFunctions { + swapForExact0Multi: SwapFunction + swapForExact1Multi: SwapFunction +} + +export function createMultiPoolFunctions({ + inputToken, + swapTarget, + poolInput, + poolOutput, +}: { + inputToken: TestERC20 + swapTarget: TestUniswapV3Router + poolInput: MockTimeUniswapV3Pool + poolOutput: MockTimeUniswapV3Pool +}): MultiPoolFunctions { + async function swapForExact0Multi(amountOut: BigNumberish, to: Wallet | string): Promise { + const method = swapTarget.swapForExact0Multi + await inputToken.approve(swapTarget.address, constants.MaxUint256) + const toAddress = typeof to === 'string' ? to : to.address + return method(toAddress, poolInput.address, poolOutput.address, amountOut) + } + + async function swapForExact1Multi(amountOut: BigNumberish, to: Wallet | string): Promise { + const method = swapTarget.swapForExact1Multi + await inputToken.approve(swapTarget.address, constants.MaxUint256) + const toAddress = typeof to === 'string' ? to : to.address + return method(toAddress, poolInput.address, poolOutput.address, amountOut) + } + + return { + swapForExact0Multi, + swapForExact1Multi, + } +} diff --git a/lib/uniswap/v3-core/tsconfig.json b/lib/uniswap/v3-core/tsconfig.json new file mode 100644 index 0000000..1522f0b --- /dev/null +++ b/lib/uniswap/v3-core/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "typeRoots": ["./typechain", "./node_modules/@types"], + "types": ["@nomiclabs/hardhat-ethers", "@nomiclabs/hardhat-waffle"] + }, + "include": ["./test"], + "files": ["./hardhat.config.ts"] +} diff --git a/lib/uniswap/v3-core/yarn.lock b/lib/uniswap/v3-core/yarn.lock new file mode 100644 index 0000000..d2e231b --- /dev/null +++ b/lib/uniswap/v3-core/yarn.lock @@ -0,0 +1,9271 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" + integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@ensdomains/ens@^0.4.4": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc" + integrity sha512-JSvpj1iNMFjK6K+uVl4unqMoa9rf5jopb8cya5UGBWz23Nw8hSNT7efgUx4BTlAPAgpNlEioUfeTyQ6J9ZvTVw== + dependencies: + bluebird "^3.5.2" + eth-ens-namehash "^2.0.8" + solc "^0.4.20" + testrpc "0.0.1" + web3-utils "^1.0.0-beta.31" + +"@ensdomains/resolver@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" + integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== + +"@ethereum-waffle/chai@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.2.1.tgz#5cb542b2a323adf0bc2dda00f48b0eb85944d8ab" + integrity sha512-0aG946J1+2Gg7WnEjtwPEWe+xwLTeBTy6LpCnGecosMf3YINkaf9Xv3Sd7CrXBR88ihCoUhfzfmOloMHIAQPAg== + dependencies: + "@ethereum-waffle/provider" "^3.2.1" + ethers "^5.0.0" + +"@ethereum-waffle/compiler@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/compiler/-/compiler-3.2.1.tgz#612a9056285a94ce28eb57b895770ad10e438bf9" + integrity sha512-URSsbTp4g8HPHAaA4KiAD6Aya9WPx/TULfOr+YpjCJ6YWXqE9wwi3ubf9qJUQxu6W6PsiczBaQhVhpI9RLoVUA== + dependencies: + "@resolver-engine/imports" "^0.3.3" + "@resolver-engine/imports-fs" "^0.3.3" + "@types/mkdirp" "^0.5.2" + "@types/node-fetch" "^2.5.5" + ethers "^5.0.1" + mkdirp "^0.5.1" + node-fetch "^2.6.0" + solc "^0.6.3" + +"@ethereum-waffle/ens@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/ens/-/ens-3.2.1.tgz#9f369112d62f7aa88d010be4d133b6d0f5e8c492" + integrity sha512-dXv/Mb8EgEYOKv2jjmkFNFCmSjNv8nPk1Gaegc0J/KXWuTU6CBOhWet7YS5joGO8ORK21MvN2qVgcQj+FYw0Dw== + dependencies: + "@ensdomains/ens" "^0.4.4" + "@ensdomains/resolver" "^0.2.4" + ethers "^5.0.1" + +"@ethereum-waffle/mock-contract@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/mock-contract/-/mock-contract-3.2.1.tgz#bf5f63f61c9749eb3270108893a88ff161e68f58" + integrity sha512-39GMp/IqsHF+3j3XK38cWA+nX7Q6ABfMsYkuAjtfpTguTFQKXx2C1/VJZwGOyb4de2pl4bssmN37VEraB3NWbQ== + dependencies: + "@ethersproject/abi" "^5.0.1" + ethers "^5.0.1" + +"@ethereum-waffle/provider@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/provider/-/provider-3.2.1.tgz#d84c0603936f09afa69ecb671d56f527e9818e71" + integrity sha512-doG18hThqldsYcZIIO0YoiwV+ERx0dCVY6bkg4FKZtoymNelf15zNycb881c6QD9RnLuz6A2Jp9lmnrAD+IvYQ== + dependencies: + "@ethereum-waffle/ens" "^3.2.1" + ethers "^5.0.1" + ganache-core "^2.10.2" + patch-package "^6.2.2" + postinstall-postinstall "^2.1.0" + +"@ethereumjs/block@^3.2.0", "@ethereumjs/block@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.2.1.tgz#c24c345e6dd6299efa4bed40979280b7dda96d3a" + integrity sha512-FCxo5KwwULne2A2Yuae4iaGGqSsRjwzXOlDhGalOFiBbLfP3hE04RHaHGw4c8vh1PfOrLauwi0dQNUBkOG3zIA== + dependencies: + "@ethereumjs/common" "^2.2.0" + "@ethereumjs/tx" "^3.1.3" + ethereumjs-util "^7.0.10" + merkle-patricia-tree "^4.1.0" + +"@ethereumjs/blockchain@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.2.1.tgz#83ed83647667265f1666f111caf065ef9d1e82b5" + integrity sha512-+hshP2qSOOFsiYvZCbaDQFG7jYTWafE8sfBi+pAsdhAHfP7BN7VLyob7qoQISgwS1s7NTR4c4+2t/woU9ahItw== + dependencies: + "@ethereumjs/block" "^3.2.0" + "@ethereumjs/common" "^2.2.0" + "@ethereumjs/ethash" "^1.0.0" + debug "^2.2.0" + ethereumjs-util "^7.0.9" + level-mem "^5.0.1" + lru-cache "^5.1.1" + rlp "^2.2.4" + semaphore-async-await "^1.5.1" + +"@ethereumjs/common@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.2.0.tgz#850a3e3e594ee707ad8d44a11e8152fb62450535" + integrity sha512-PyQiTG00MJtBRkJmv46ChZL8u2XWxNBeAthznAUIUiefxPAXjbkuiCZOuncgJS34/XkMbNc9zMt/PlgKRBElig== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.0.9" + +"@ethereumjs/ethash@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.0.0.tgz#4e77f85b37be1ade5393e8719bdabac3e796ddaa" + integrity sha512-iIqnGG6NMKesyOxv2YctB2guOVX18qMAWlj3QlZyrc+GqfzLqoihti+cVNQnyNxr7eYuPdqwLQOFuPe6g/uKjw== + dependencies: + "@types/levelup" "^4.3.0" + buffer-xor "^2.0.1" + ethereumjs-util "^7.0.7" + miller-rabin "^4.0.0" + +"@ethereumjs/tx@^3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.1.3.tgz#0e4b0ccec2f12b1f0bbbb0e7542dd79d9ec25d87" + integrity sha512-DJBu6cbwYtiPTFeCUR8DF5p+PF0jxs+0rALJZiEcTz2tiRPIEkM72GEbrkGuqzENLCzBrJHT43O0DxSYTqeo+g== + dependencies: + "@ethereumjs/common" "^2.2.0" + ethereumjs-util "^7.0.10" + +"@ethereumjs/vm@^5.3.2": + version "5.3.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.3.2.tgz#b4d83a3d50a7ad22d6d412cc21bbde221b3e2871" + integrity sha512-QmCUQrW6xbhgEbQh9njue4kAJdM056C+ytBFUTF/kDYa3kNDm4Qxp9HUyTlt1OCSXvDhws0qqlh8+q+pmXpN7g== + dependencies: + "@ethereumjs/block" "^3.2.1" + "@ethereumjs/blockchain" "^5.2.1" + "@ethereumjs/common" "^2.2.0" + "@ethereumjs/tx" "^3.1.3" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^2.2.0" + ethereumjs-util "^7.0.10" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.1.0" + rustbn.js "~0.2.0" + util.promisify "^1.0.1" + +"@ethersproject/abi@5.0.0-beta.153": + version "5.0.0-beta.153" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" + integrity sha512-aXweZ1Z7vMNzJdLpR1CZUAIgnwjrZeUSvN9syCwlBaEBUFJmFY+HHnfuTI5vIhVs/mRkfJVrbEyl51JZQqyjAg== + dependencies: + "@ethersproject/address" ">=5.0.0-beta.128" + "@ethersproject/bignumber" ">=5.0.0-beta.130" + "@ethersproject/bytes" ">=5.0.0-beta.129" + "@ethersproject/constants" ">=5.0.0-beta.128" + "@ethersproject/hash" ">=5.0.0-beta.128" + "@ethersproject/keccak256" ">=5.0.0-beta.127" + "@ethersproject/logger" ">=5.0.0-beta.129" + "@ethersproject/properties" ">=5.0.0-beta.131" + "@ethersproject/strings" ">=5.0.0-beta.130" + +"@ethersproject/abi@5.0.9", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.0.5": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.9.tgz#738c1c557e56d8f395a5a27caef9b0449bc85a10" + integrity sha512-ily2OufA2DTrxkiHQw5GqbkMSnNKuwZBqKsajtT0ERhZy1r9w2CpW1bmtRMIGzaqQxCdn/GEoFogexk72cBBZQ== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abi@^5.1.2": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" + integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/abstract-provider@5.0.7", "@ethersproject/abstract-provider@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.7.tgz#04ee3bfe43323384e7fecf6c774975b8dec4bdc9" + integrity sha512-NF16JGn6M0zZP5ZS8KtDL2Rh7yHxZbUjBIHLNHMm/0X0BephhjUWy8jqs/Zks6kDJRzNthgmPVy41Ec0RYWPYA== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + +"@ethersproject/abstract-provider@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" + integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + +"@ethersproject/abstract-signer@5.0.9", "@ethersproject/abstract-signer@^5.0.4", "@ethersproject/abstract-signer@^5.0.6": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.9.tgz#238ddc06031aeb9dfceee2add965292d7dd1acbf" + integrity sha512-CM5UNmXQaA03MyYARFDDRjHWBxujO41tVle7glf5kHcQsDDULgqSVpkliLJMtPzZjOKFeCVZBHybTZDEZg5zzg== + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/abstract-signer@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" + integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/address@5.0.8", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.8.tgz#0c551659144a5a7643c6bea337149d410825298f" + integrity sha512-V87DHiZMZR6hmFYmoGaHex0D53UEbZpW75uj8AqPbjYUmi65RB4N2LPRcJXuWuN2R0Y2CxkvW6ArijWychr5FA== + dependencies: + "@ethersproject/bignumber" "^5.0.10" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/rlp" "^5.0.3" + +"@ethersproject/address@^5.0.2": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.1.0.tgz#3854fd7ebcb6af7597de66f847c3345dae735b58" + integrity sha512-rfWQR12eHn2cpstCFS4RF7oGjfbkZb0oqep+BfrT+gWEGWG2IowJvIsacPOvzyS1jhNF4MQ4BS59B04Mbovteg== + dependencies: + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/keccak256" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/rlp" "^5.1.0" + +"@ethersproject/address@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" + integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + +"@ethersproject/base64@5.0.6", "@ethersproject/base64@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.6.tgz#26311ebf29ea3d0b9c300ccf3e1fdc44b7481516" + integrity sha512-HwrGn8YMiUf7bcdVvB4NJ+eWT0BtEFpDtrYxVXEbR7p/XBSJjwiR7DEggIiRvxbualMKg+EZijQWJ3az2li0uw== + dependencies: + "@ethersproject/bytes" "^5.0.4" + +"@ethersproject/base64@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" + integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + +"@ethersproject/basex@5.0.6", "@ethersproject/basex@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.6.tgz#ab95c32e48288a3d868726463506641cb1e9fb6b" + integrity sha512-Y/8dowRxBF3bsKkqEp7XN4kcFFQ0o5xxP1YyopfqkXejaOEGiD7ToQdQ0pIZpAJ5GreW56oFOTDDSO6ZcUCNYg== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/bignumber@5.0.12", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.10", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.0.8": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.12.tgz#fe4a78667d7cb01790f75131147e82d6ea7e7cba" + integrity sha512-mbFZjwthx6vFlHG9owXP/C5QkNvsA+xHpDCkPPPdG2n1dS9AmZAL5DI0InNLid60rQWL3MXpEl19tFmtL7Q9jw== + dependencies: + "@ethersproject/bytes" "^5.0.8" + "@ethersproject/logger" "^5.0.5" + bn.js "^4.4.0" + +"@ethersproject/bignumber@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.1.1.tgz#84812695253ccbc639117f7ac49ee1529b68e637" + integrity sha512-AVz5iqz7+70RIqoQTznsdJ6DOVBYciNlvO+AlQmPTB6ofCvoihI9bQdr6wljsX+d5W7Yc4nyvQvP4JMzg0Agig== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + bn.js "^4.4.0" + +"@ethersproject/bignumber@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" + integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + bn.js "^4.11.9" + +"@ethersproject/bytes@5.0.8", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.8.tgz#cf1246a6a386086e590063a4602b1ffb6cc43db1" + integrity sha512-O+sJNVGzzuy51g+EMK8BegomqNIg+C2RO6vOt0XP6ac4o4saiq69FnjlsrNslaiMFVO7qcEHBsWJ9hx1tj1lMw== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/bytes@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.1.0.tgz#55dfa9c4c21df1b1b538be3accb50fb76d5facfd" + integrity sha512-sGTxb+LVjFxJcJeUswAIK6ncgOrh3D8c192iEJd7mLr95V6du119rRfYT/b87WPkZ5I3gRBUYIYXtdgCWACe8g== + dependencies: + "@ethersproject/logger" "^5.1.0" + +"@ethersproject/bytes@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/constants@5.0.7", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.7.tgz#44ff979e5781b17c8c6901266896c3ee745f4e7e" + integrity sha512-cbQK1UpE4hamB52Eg6DLhJoXeQ1plSzekh5Ujir1xdREdwdsZPPXKczkrWqBBR0KyywJZHN/o/hj0w8j7scSGg== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + +"@ethersproject/constants@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" + integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + +"@ethersproject/contracts@5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.8.tgz#71d3ba16853a1555be2e161a6741df186f81c73b" + integrity sha512-PecBL4vnsrpuks2lzzkRsOts8csJy338HNDKDIivbFmx92BVzh3ohOOv3XsoYPSXIHQvobF959W+aSk3RCZL/g== + dependencies: + "@ethersproject/abi" "^5.0.5" + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/hash@5.0.9", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.9.tgz#81252a848185b584aa600db4a1a68cad9229a4d4" + integrity sha512-e8/i2ZDeGSgCxXT0vocL54+pMbw5oX5fNjb2E3bAIvdkh5kH29M7zz1jHu1QDZnptIuvCZepIbhUH8lxKE2/SQ== + dependencies: + "@ethersproject/abstract-signer" "^5.0.6" + "@ethersproject/address" "^5.0.5" + "@ethersproject/bignumber" "^5.0.8" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.4" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/hash@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" + integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/hdnode@5.0.7", "@ethersproject/hdnode@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.7.tgz#c7bce94a337ea65e37c46bab09a83e1c1a555d99" + integrity sha512-89tphqlji4y/LNE1cSaMQ3hrBtJ4lO1qWGi2hn54LiHym85DTw+zAKbA8QgmdSdJDLGR/kc9VHaIPQ+vZQ2LkQ== + dependencies: + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/basex" "^5.0.3" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/pbkdf2" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/wordlists" "^5.0.4" + +"@ethersproject/json-wallets@5.0.9", "@ethersproject/json-wallets@^5.0.6": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.9.tgz#2e1708c2854c4ab764e35920bd1f44c948b95434" + integrity sha512-EWuFvJd8nu90dkmJwmJddxOYCvFvMkKBsZi8rxTme2XEZsHKOFnybVkoL23u7ZtApuEfTKmVcR2PTwgZwqDsKw== + dependencies: + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/hdnode" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/pbkdf2" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.0.6", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.6.tgz#5b5ba715ef1be86efde5c271f896fa0daf0e1efe" + integrity sha512-eJ4Id/i2rwrf5JXEA7a12bG1phuxjj47mPZgDUbttuNBodhSuZF2nEO5QdpaRjmlphQ8Kt9PNqY/z7lhtJptZg== + dependencies: + "@ethersproject/bytes" "^5.0.4" + js-sha3 "0.5.7" + +"@ethersproject/keccak256@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.1.0.tgz#fdcd88fb13bfef4271b225cdd8dec4d315c8e60e" + integrity sha512-vrTB1W6AEYoadww5c9UyVJ2YcSiyIUTNDRccZIgwTmFFoSHwBtcvG1hqy9RzJ1T0bMdATbM9Hfx2mJ6H0i7Hig== + dependencies: + "@ethersproject/bytes" "^5.1.0" + js-sha3 "0.5.7" + +"@ethersproject/keccak256@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" + integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.0.8", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.8.tgz#135c1903d35c878265f3cbf2b287042c4c20d5d4" + integrity sha512-SkJCTaVTnaZ3/ieLF5pVftxGEFX56pTH+f2Slrpv7cU0TNpUZNib84QQdukd++sWUp/S7j5t5NW+WegbXd4U/A== + +"@ethersproject/logger@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.1.0.tgz#4cdeeefac029373349d5818f39c31b82cc6d9bbf" + integrity sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw== + +"@ethersproject/logger@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + +"@ethersproject/networks@5.0.6", "@ethersproject/networks@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.6.tgz#4d6586bbebfde1c027504ebf6dfb783b29c3803a" + integrity sha512-2Cg1N5109zzFOBfkyuPj+FfF7ioqAsRffmybJ2lrsiB5skphIAE72XNSCs4fqktlf+rwSh/5o/UXRjXxvSktZw== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/networks@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" + integrity sha512-KWfP3xOnJeF89Uf/FCJdV1a2aDJe5XTN2N52p4fcQ34QhDqQFkgQKZ39VGtiqUgHcLI8DfT0l9azC3KFTunqtA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/pbkdf2@5.0.6", "@ethersproject/pbkdf2@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.6.tgz#105dbfb08cd5fcf33869b42bfdc35a3ebd978cbd" + integrity sha512-CUYciSxR/AaCoKMJk3WUW+BDhR41G3C+O9lOeZ4bR1wDhLKL2Z8p0ciF5XDEiVbmI4CToW6boVKybeVMdngRrg== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/sha2" "^5.0.3" + +"@ethersproject/properties@5.0.6", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.4": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.6.tgz#44d82aaa294816fd63333e7def42426cf0e87b3b" + integrity sha512-a9DUMizYhJ0TbtuDkO9iYlb2CDlpSKqGPDr+amvlZhRspQ6jbl5Eq8jfu4SCcGlcfaTbguJmqGnyOGn1EFt6xA== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/properties@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" + integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/providers@5.0.17": + version "5.0.17" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.17.tgz#f380e7831149e24e7a1c6c9b5fb1d6dfc729d024" + integrity sha512-bJnvs5X7ttU5x2ekGJYG7R3Z+spZawLFfR0IDsbaMDLiCwZOyrgk+VTBU7amSFLT0WUhWFv8WwSUB+AryCQG1Q== + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/basex" "^5.0.3" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + bech32 "1.1.4" + ws "7.2.3" + +"@ethersproject/random@5.0.6", "@ethersproject/random@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.6.tgz#9be80a1065f2b8e6f321dccb3ebeb4886cac9ea4" + integrity sha512-8nsVNaZvZ9OD5NXfzE4mmz8IH/1DYJbAR95xpRxZkIuNmfn6QlMp49ccJYZWGhs6m0Zj2+FXjx3pzXfYlo9/dA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/rlp@5.0.6", "@ethersproject/rlp@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.6.tgz#29f9097348a3c330811997433b7df89ab51cd644" + integrity sha512-M223MTaydfmQSsvqAl0FJZDYFlSqt6cgbhnssLDwqCKYegAHE16vrFyo+eiOapYlt32XAIJm0BXlqSunULzZuQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/rlp@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.1.0.tgz#700f4f071c27fa298d3c1d637485fefe919dd084" + integrity sha512-vDTyHIwNPrecy55gKGZ47eJZhBm8LLBxihzi5ou+zrSvYTpkSTWRcKUlXFDFQVwfWB+P5PGyERAdiDEI76clxw== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + +"@ethersproject/rlp@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" + integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/sha2@5.0.6", "@ethersproject/sha2@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.6.tgz#175116dc10b866a0a381f6316d094bcc510bee3c" + integrity sha512-30gypDLkfkP5gE3llqi0jEuRV8m4/nvzeqmqMxiihZ7veFQHqDaGpyFeHzFim+qGeH9fq0lgYjavLvwW69+Fkw== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + hash.js "1.1.3" + +"@ethersproject/signing-key@5.0.7", "@ethersproject/signing-key@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.7.tgz#d03bfc5f565efb962bafebf8e6965e70d1c46d31" + integrity sha512-JYndnhFPKH0daPcIjyhi+GMcw3srIHkQ40hGRe6DA0CdGrpMfgyfSYDQ2D8HL2lgR+Xm4SHfEB0qba6+sCyrvg== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + elliptic "6.5.3" + +"@ethersproject/signing-key@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" + integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.7.tgz#72a3455f47a454db2dcf363992d42e9045dc7fce" + integrity sha512-dUevKUZ06p/VMLP/+cz4QUV+lA17NixucDJfm0ioWF0B3R0Lf+6wqwPchcqiAXlxkNFGIax7WNLgGMh4CkQ8iw== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/strings@5.0.7", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.7.tgz#8dc68f794c9e2901f3b75e53b2afbcb6b6c15037" + integrity sha512-a+6T80LvmXGMOOWQTZHtGGQEg1z4v8rm8oX70KNs55YtPXI/5J3LBbVf5pyqCKSlmiBw5IaepPvs5XGalRUSZQ== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/strings@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" + integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/transactions@5.0.8", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.8.tgz#3b4d7041e13b957a9c4f131e0aea9dae7b6f5a23" + integrity sha512-i7NtOXVzUe+YSU6QufzlRrI2WzHaTmULAKHJv4duIZMLqzehCBXGA9lTpFgFdqGYcQJ7vOtNFC2BB2mSjmuXqg== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + +"@ethersproject/transactions@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" + integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + +"@ethersproject/units@5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.8.tgz#563325b20fe1eceff7b61857711d5e2b3f38fd09" + integrity sha512-3O4MaNHFs05vC5v2ZGqVFVWtO1WyqFejO78M7Qh16njo282aoMlENtVI6cn2B36zOLFXRvYt2pYx6xCG53qKzg== + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/wallet@5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.9.tgz#976c7d950489c40308d676869d24e59ab7b82ad1" + integrity sha512-GfpQF56PO/945SJq7Wdg5F5U6wkxaDgkAzcgGbCW6Joz8oW8MzKItkvYCzMh+j/8gJMzFncsuqX4zg2gq3J6nQ== + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/hdnode" "^5.0.4" + "@ethersproject/json-wallets" "^5.0.6" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/wordlists" "^5.0.4" + +"@ethersproject/web@5.0.11", "@ethersproject/web@^5.0.6": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.11.tgz#d47da612b958b4439e415782a53c8f8461522d68" + integrity sha512-x03ihbPoN1S8Gsh9WSwxkYxUIumLi02ZEKJku1C43sxBfe+mdprWyvujzYlpuoRNfWRgNhdRDKMP8JbG6MwNGA== + dependencies: + "@ethersproject/base64" "^5.0.3" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/web@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.0.tgz#0e5bb21a2b58fb4960a705bfc6522a6acf461e28" + integrity sha512-BEgY0eL5oH4mAo37TNYVrFeHsIXLRxggCRG/ksRIxI2X5uj5IsjGmcNiRN/VirQOlBxcUhCgHhaDLG4m6XAVoA== + dependencies: + "@ethersproject/base64" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/wordlists@5.0.7", "@ethersproject/wordlists@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.7.tgz#4e5ad38cfbef746b196a3290c0d41696eb7ab468" + integrity sha512-ZjQtYxm41FmHfYgpkdQG++EDcBPQWv9O6FfP6NndYRVaXaQZh6eq3sy7HQP8zCZ8dznKgy6ZyKECS8qdvnGHwA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/test-result@^26.5.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@nomiclabs/hardhat-ethers@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" + integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== + +"@nomiclabs/hardhat-etherscan@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.8.tgz#e206275e96962cd15e5ba9148b44388bc922d8c2" + integrity sha512-0+rj0SsZotVOcTLyDOxnOc3Gulo8upo0rsw/h+gBPcmtj91YqYJNhdARHoBxOhhE8z+5IUQPx+Dii04lXT14PA== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^5.0.2" + debug "^4.1.1" + fs-extra "^7.0.1" + node-fetch "^2.6.0" + semver "^6.3.0" + +"@nomiclabs/hardhat-waffle@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.1.tgz#5d43654fba780720c5033dea240fe14f70ef4bd2" + integrity sha512-2YR2V5zTiztSH9n8BYWgtv3Q+EL0N5Ltm1PAr5z20uAY4SkkfylJ98CIqt18XFvxTD5x4K2wKBzddjV9ViDAZQ== + dependencies: + "@types/sinon-chai" "^3.2.3" + "@types/web3" "1.0.19" + +"@resolver-engine/core@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967" + integrity sha512-eB8nEbKDJJBi5p5SrvrvILn4a0h42bKtbCTri3ZxCGt6UvoQyp7HnGOfki944bUjBSHKK3RvgfViHn+kqdXtnQ== + dependencies: + debug "^3.1.0" + is-url "^1.2.4" + request "^2.85.0" + +"@resolver-engine/fs@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/fs/-/fs-0.3.3.tgz#fbf83fa0c4f60154a82c817d2fe3f3b0c049a973" + integrity sha512-wQ9RhPUcny02Wm0IuJwYMyAG8fXVeKdmhm8xizNByD4ryZlx6PP6kRen+t/haF43cMfmaV7T3Cx6ChOdHEhFUQ== + dependencies: + "@resolver-engine/core" "^0.3.3" + debug "^3.1.0" + +"@resolver-engine/imports-fs@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/imports-fs/-/imports-fs-0.3.3.tgz#4085db4b8d3c03feb7a425fbfcf5325c0d1e6c1b" + integrity sha512-7Pjg/ZAZtxpeyCFlZR5zqYkz+Wdo84ugB5LApwriT8XFeQoLwGUj4tZFFvvCuxaNCcqZzCYbonJgmGObYBzyCA== + dependencies: + "@resolver-engine/fs" "^0.3.3" + "@resolver-engine/imports" "^0.3.3" + debug "^3.1.0" + +"@resolver-engine/imports@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/imports/-/imports-0.3.3.tgz#badfb513bb3ff3c1ee9fd56073e3144245588bcc" + integrity sha512-anHpS4wN4sRMwsAbMXhMfOD/y4a4Oo0Cw/5+rue7hSwGWsDOQaAU1ClK1OxjUC35/peazxEl8JaSRRS+Xb8t3Q== + dependencies: + "@resolver-engine/core" "^0.3.3" + debug "^3.1.0" + hosted-git-info "^2.6.0" + path-browserify "^1.0.0" + url "^0.11.0" + +"@sentry/core@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.29.2.tgz#9e05fe197234161d57aabaf52fab336a7c520d81" + integrity sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw== + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/minimal" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/hub@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.29.2.tgz#208f10fe6674695575ad74182a1151f71d6df00a" + integrity sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ== + dependencies: + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/minimal@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.29.2.tgz#420bebac8d03d30980fdb05c72d7b253d8aa541b" + integrity sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw== + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/types" "5.29.2" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.29.2.tgz#f0f0b4b2be63c9ddd702729fab998cead271dff1" + integrity sha512-98m1ZejmJgA+eiz6jEFyYYfp6kJZQnx6d6KrJDMxGfss4YTmmJY57bE4xStnjjk7WINDGzlCiHuk+wJFMBjuoA== + dependencies: + "@sentry/core" "5.29.2" + "@sentry/hub" "5.29.2" + "@sentry/tracing" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.29.2.tgz#6012788547d2ab7893799d82c4941bda145dcd47" + integrity sha512-iumYbVRpvoU3BUuIooxibydeaOOjl5ysc+mzsqhRs2NGW/C3uKAsFXdvyNfqt3bxtRQwJEhwJByLP2u3pLThpw== + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/minimal" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/types@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.29.2.tgz#ac87383df1222c2d9b9f8f9ed7a6b86ea41a098a" + integrity sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA== + +"@sentry/utils@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.29.2.tgz#99a5cdda2ea19d34a41932f138d470adcb3ee673" + integrity sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ== + dependencies: + "@sentry/types" "5.29.2" + tslib "^1.9.3" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@solidity-parser/parser@^0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.10.1.tgz#d3680d1ebebed21eee67f58a41eb92175204f0c7" + integrity sha512-tHDPCRMEBFDxBz5rioQRoKgOQGa/K2digdfR68cd5vO6IufAqoNt1sfjssQDf2KPqHPftICBQOqlcu0w5/Jisg== + +"@solidity-parser/parser@^0.11.0": + version "0.11.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" + integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== + +"@solidity-parser/parser@^0.8.2": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.8.2.tgz#a6a5e93ac8dca6884a99a532f133beba59b87b69" + integrity sha512-8LySx3qrNXPgB5JiULfG10O3V7QTxI/TLzSw5hFQhXWSkVxZBAv4rZQ0sYgLEbc8g3L2lmnujj1hKul38Eu5NQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@typechain/ethers-v5@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-4.0.0.tgz#2a8be5e108d23f3b8e6354d1618fdc2abcb00b07" + integrity sha512-Rw4WHPIuwTXWcHfmn9ICQISQhmJa6Ug5IjqPYLpsKqlED2L4W2JgQ6S9hYt4IKVmT//1yxIxD+iaa5tSQEEx1A== + +"@types/abstract-leveldown@*": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.1.tgz#3c7750d0186b954c7f2d2f6acc8c3c7ba0c3412e" + integrity sha512-wYxU3kp5zItbxKmeRYCEplS2MW7DzyBnxPGj+GJVHZEUZiK/nn5Ei1sUFgURDh+X051+zsGe28iud3oHjrYWQQ== + +"@types/babel__traverse@^7.0.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.0.tgz#b9a1efa635201ba9bc850323a8793ee2d36c04a0" + integrity sha512-kSjgDMZONiIfSH1Nxcr5JIRMwUetDki63FSQfpTCz8ogF3Ulqm8+mr5f78dUYs6vMiB6gBusQqfQmBvHZj/lwg== + dependencies: + "@babel/types" "^7.3.0" + +"@types/bn.js@*", "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/chai@*", "@types/chai@^4.2.6": + version "4.2.14" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193" + integrity sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ== + +"@types/graceful-fs@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz#4ff9f641a7c6d1a3508ff88bc3141b152772e753" + integrity sha512-mWA/4zFQhfvOA8zWkXobwJvBD7vzcxgrOQ0J5CH1votGqdq9m7+FwtGaqyCZqC3NyyBkc9z4m+iry4LlqcMWJg== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/levelup@^4.3.0": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.1.tgz#7a53b9fd510716e11b2065332790fdf5f9b950b9" + integrity sha512-n//PeTpbHLjMLTIgW5B/g06W/6iuTBHuvUka2nFL9APMSVMNe2r4enADfu3CIE9IyV9E+uquf9OEQQqrDeg24A== + dependencies: + "@types/abstract-leveldown" "*" + "@types/node" "*" + +"@types/lru-cache@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" + integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== + +"@types/mkdirp@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" + integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== + dependencies: + "@types/node" "*" + +"@types/mocha@^5.2.7": + version "5.2.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" + integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== + +"@types/node-fetch@^2.5.5": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" + integrity sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "14.14.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae" + integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ== + +"@types/node@^12.12.6": + version "12.19.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679" + integrity sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q== + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/prettier@^2.0.0", "@types/prettier@^2.1.1": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00" + integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ== + +"@types/resolve@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" + integrity sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog== + dependencies: + "@types/node" "*" + +"@types/sinon-chai@^3.2.3": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48" + integrity sha512-bKQqIpew7mmIGNRlxW6Zli/QVyc3zikpGzCa797B/tRnD9OtHvZ/ts8sYXV+Ilj9u3QRaUEM8xrjgd1gwm1BpQ== + dependencies: + "@types/chai" "*" + "@types/sinon" "*" + +"@types/sinon@*": + version "9.0.10" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.10.tgz#7fb9bcb6794262482859cab66d59132fca18fcf7" + integrity sha512-/faDC0erR06wMdybwI/uR8wEKV/E83T0k4sepIpB7gXuy2gzx2xiOjmztq6a2Y6rIGJ04D+6UU0VBmWy+4HEMA== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" + integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== + +"@types/underscore@*": + version "1.10.24" + resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.10.24.tgz#dede004deed3b3f99c4db0bdb9ee21cae25befdd" + integrity sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w== + +"@types/web3@1.0.19": + version "1.0.19" + resolved "https://registry.yarnpkg.com/@types/web3/-/web3-1.0.19.tgz#46b85d91d398ded9ab7c85a5dd57cb33ac558924" + integrity sha512-fhZ9DyvDYDwHZUp5/STa9XW2re0E8GxoioYJ4pEUZ13YHpApSagixj7IAdoYH5uAK+UalGq6Ml8LYzmgRA/q+A== + dependencies: + "@types/bn.js" "*" + "@types/underscore" "*" + +"@types/yargs-parser@*": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== + +"@types/yargs@^15.0.0": + version "15.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.12.tgz#6234ce3e3e3fa32c5db301a170f96a599c960d74" + integrity sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw== + dependencies: + "@types/yargs-parser" "*" + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abstract-leveldown@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" + integrity sha512-KUWx9UWGQD12zsmLNj64/pndaz4iJh/Pj7nopgkfDG6RlCcbMZvT6+9l7dchK4idog2Is8VdC/PvNbFuFmalIQ== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^2.4.1, abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz#f7128e1f86ccabf7d2893077ce5d06d798e386c6" + integrity sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-jsx@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3, anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-back@^1.0.3, array-back@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b" + integrity sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs= + dependencies: + typical "^2.6.0" + +array-back@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-2.0.0.tgz#6877471d51ecc9c9bfa6136fb6c7d5fe69748022" + integrity sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw== + dependencies: + typical "^2.6.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== + dependencies: + lodash "^4.17.11" + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.0.14, babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ= + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340= + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo= + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk= + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI= + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo= + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM= + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E= + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8= + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs= + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ= + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM= + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg= + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40= + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys= + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw= + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek= + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4= + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8= + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg== + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babelify@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" + integrity sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU= + dependencies: + babel-core "^6.0.14" + object-assign "^4.0.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + +bip39@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235" + integrity sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA== + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + safe-buffer "^5.0.1" + unorm "^1.3.3" + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +bluebird@^3.5.0, bluebird@^3.5.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^4.8.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer-xor@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" + integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== + dependencies: + safe-buffer "^5.1.1" + +buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.2.tgz#79f68631910f6b993d870fc77dc0a2894eb96cd5" + integrity sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA== + dependencies: + node-gyp-build "^4.2.0" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +bytewise-core@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bytewise-core/-/bytewise-core-1.2.3.tgz#3fb410c7e91558eb1ab22a82834577aa6bd61d42" + integrity sha1-P7QQx+kVWOsasiqCg0V3qmvWHUI= + dependencies: + typewise-core "^1.2" + +bytewise@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/bytewise/-/bytewise-1.1.0.tgz#1d13cbff717ae7158094aa881b35d081b387253e" + integrity sha1-HRPL/3F65xWAlKqIGzXQgbOHJT4= + dependencies: + bytewise-core "^1.2.2" + typewise "^1.0.3" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +cachedown@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cachedown/-/cachedown-1.0.0.tgz#d43f036e4510696b31246d7db31ebf0f7ac32d15" + integrity sha1-1D8DbkUQaWsxJG19sx6/D3rDLRU= + dependencies: + abstract-leveldown "^2.4.1" + lru-cache "^3.2.0" + +call-bind@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" + integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.0" + +call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30000844: + version "1.0.30001170" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7" + integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cbor@^5.0.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +chokidar@^3.4.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@2.1.2, clone@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +command-line-args@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46" + integrity sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA== + dependencies: + array-back "^2.0.0" + find-replace "^1.0.3" + typical "^2.6.1" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-pure@^3.0.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.1.tgz#23f84048f366fdfcf52d3fd1c68fec349177d119" + integrity sha512-Se+LaxqXlVXGvmexKGPvnUIYC1jwXu1H6Pkyb3uBM5d8/NELMYCHs/4/roD7721NxrTLyv7e5nXd5/QLBO+10g== + +core-js@^2.4.0, core-js@^2.5.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +crc-32@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@4, debug@^4.0.1, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decimal.js@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-equal@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +deferred-leveldown@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz#0b0570087827bf480a23494b398f04c128c19a20" + integrity sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww== + dependencies: + abstract-leveldown "~5.0.0" + inherits "^2.0.3" + +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-to-object@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-to-object/-/dir-to-object-2.0.0.tgz#29723e9bd1c3e58e4f307bd04ff634c0370c8f8a" + integrity sha512-sXs0JKIhymON7T1UZuO2Ud6VTNAx/VTBXIl4+3mjb2RgfOpt+hectX0x04YqPOPdkeOAKoJuKqwqnXXURNPNEA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +dotignore@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" + integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== + dependencies: + minimatch "^3.0.4" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.47: + version "1.3.633" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz#16dd5aec9de03894e8d14a1db4cda8a369b9b7fe" + integrity sha512-bsVCsONiVX1abkWdH7KtpuDAhsQ3N3bjPYhROSAXE78roJKet0Y5wznA14JE9pzbwSZmSMAW6KiKYf1RvbTJkA== + +elliptic@6.5.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.0.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.0.tgz#a26da8e832b16a9753309f25e35e3c0efb9a066a" + integrity sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding-down@5.0.4, encoding-down@~5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614" + integrity sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw== + dependencies: + abstract-leveldown "^5.0.0" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + xtend "^4.0.1" + +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enquirer@^2.3.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: + version "1.17.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" + integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.2: + version "1.18.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" + integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.2" + is-string "^1.0.5" + object-inspect "^1.9.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" + integrity sha512-WUVxWLuhMmsfenfZvFO5sbl1qFY2IqUlw/FPVmjjdElpqLsZtSG+wPe9Dz7W/sB6e80HgFKknOmKk2eNlznHug== + dependencies: + eth-query "^2.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.3" + ethjs-util "^0.1.3" + json-rpc-engine "^3.6.0" + pify "^2.3.0" + tape "^4.6.3" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-infura@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.1.tgz#26702a821067862b72d979c016fd611502c6057f" + integrity sha512-W7zR4DZvyTn23Bxc0EWsq4XGDdD63+XPUCEhV2zQvQGavDVC4ZpFDK4k99qN7bd7/fjj37+rxmuBOBeIqCA5Mw== + dependencies: + cross-fetch "^2.1.1" + eth-json-rpc-middleware "^1.5.0" + json-rpc-engine "^3.4.0" + json-rpc-error "^2.0.0" + +eth-json-rpc-middleware@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" + integrity sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q== + dependencies: + async "^2.5.0" + eth-query "^2.1.2" + eth-tx-summary "^3.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.1.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^3.6.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + tape "^4.6.3" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-sig-util@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-1.4.2.tgz#8d958202c7edbaae839707fba6f09ff327606210" + integrity sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA= + dependencies: + ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" + ethereumjs-util "^5.1.1" + +eth-sig-util@^2.0.0, eth-sig-util@^2.5.2: + version "2.5.3" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.5.3.tgz#6938308b38226e0b3085435474900b03036abcbe" + integrity sha512-KpXbCKmmBUNUTGh9MRKmNkIPietfhzBqqYqysDavLseIiMUGl95k6UcPEkALAZlj41e9E6yioYXc1PC333RKqw== + dependencies: + buffer "^5.2.1" + elliptic "^6.4.0" + ethereumjs-abi "0.6.5" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.0" + tweetnacl-util "^0.15.0" + +eth-tx-summary@^3.1.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c" + integrity sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg== + dependencies: + async "^2.1.2" + clone "^2.0.0" + concat-stream "^1.5.1" + end-of-stream "^1.1.0" + eth-query "^2.0.2" + ethereumjs-block "^1.4.1" + ethereumjs-tx "^1.1.1" + ethereumjs-util "^5.0.1" + ethereumjs-vm "^2.6.0" + through2 "^2.0.3" + +ethashjs@~0.0.7: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ethashjs/-/ethashjs-0.0.8.tgz#227442f1bdee409a548fb04136e24c874f3aa6f9" + integrity sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw== + dependencies: + async "^2.1.2" + buffer-xor "^2.0.1" + ethereumjs-util "^7.0.2" + miller-rabin "^4.0.0" + +ethereum-bloom-filters@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz#b7b80735e385dbb7f944ce6b4533e24511306060" + integrity sha512-cDcJJSJ9GMAcURiAWO3DxIEhTL/uWqlQnvgKpuYQzYPrt/izuGU+1ntQmHt0IRq6ADoSYHFnB+aCEFIldjhkMQ== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-waffle@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ethereum-waffle/-/ethereum-waffle-3.2.1.tgz#9d6d6b93484c5e1b77dfdeb646c050ed877e836e" + integrity sha512-Fhg7BaBuV+Xo5XT+NEC3UTKGunvpq+iQPglZbIAJF6ZcwQwkiKfJUDuB0ZSkg5ntbRS4gpahfoXj1nTzdtx8UA== + dependencies: + "@ethereum-waffle/chai" "^3.2.1" + "@ethereum-waffle/compiler" "^3.2.1" + "@ethereum-waffle/mock-contract" "^3.2.1" + "@ethereum-waffle/provider" "^3.2.1" + ethers "^5.0.1" + +ethereumjs-abi@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" + integrity sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE= + dependencies: + bn.js "^4.10.0" + ethereumjs-util "^4.3.0" + +ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": + version "0.6.8" + resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f" + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@3.0.0, ethereumjs-account@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz#728f060c8e0c6e87f1e987f751d3da25422570a9" + integrity sha512-WP6BdscjiiPkQfF9PVfMcwx/rDvfZTjFKY0Uwc09zSQr9JfIVH87dYIJu0gNhBhpmovV4yq295fdllS925fnBA== + dependencies: + ethereumjs-util "^6.0.0" + rlp "^2.2.1" + safe-buffer "^5.1.1" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@2.2.2, ethereumjs-block@^2.2.2, ethereumjs-block@~2.2.0, ethereumjs-block@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-blockchain@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.4.tgz#30f2228dc35f6dcf94423692a6902604ae34960f" + integrity sha512-zCxaRMUOzzjvX78DTGiKjA+4h2/sF0OYL1QuPux0DHpyq8XiNoF5GYHtb++GUxVlMsMfZV7AVyzbtgcRdIcEPQ== + dependencies: + async "^2.6.1" + ethashjs "~0.0.7" + ethereumjs-block "~2.2.2" + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.1.0" + flow-stoplight "^1.0.0" + level-mem "^3.0.1" + lru-cache "^5.1.1" + rlp "^2.2.2" + semaphore "^1.1.0" + +ethereumjs-common@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz#d3e82fc7c47c0cef95047f431a99485abc9bb1cd" + integrity sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ== + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-tx@2.1.2, ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^4.3.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.1.tgz#f4bf9b3b515a484e3cc8781d61d9d980f7c83bd0" + integrity sha512-WrckOZ7uBnei4+AKimpuF1B3Fv25OmoRgmYCpGsP7u8PFxXAmAgiJSYT2kRWnt6fVIlKaQlZvuwXp7PIrmn3/w== + dependencies: + bn.js "^4.8.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + rlp "^2.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.7, ethereumjs-util@^7.0.8, ethereumjs-util@^7.0.9: + version "7.0.10" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" + integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-util@^7.0.2: + version "7.0.7" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.7.tgz#484fb9c03b766b2ee64821281070616562fb5a59" + integrity sha512-vU5rtZBlZsgkTw3o6PDKyB8li2EgLavnAbsKcfsH2YhHH1Le+PP8vEiMnAnvgc1B6uMoaM5GDCrVztBw0Q5K9g== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-vm@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz#e885e861424e373dbc556278f7259ff3fca5edab" + integrity sha512-X6qqZbsY33p5FTuZqCnQ4+lo957iUJMM6Mpa6bL4UW0dxM6WmDSHuI4j/zOp1E2TDKImBGCJA9QPfc08PaNubA== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + +ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz#685e9091645cee230ad125c007658833991ed474" + integrity sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^6.0.0" + randombytes "^2.0.6" + safe-buffer "^5.1.2" + scryptsy "^1.2.1" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.8: + version "5.0.24" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.24.tgz#fbb8e4d35070d134f2eb846c07500b8c0eaef6d3" + integrity sha512-77CEtVC88fJGEhxGXRvQqAEH6e2A+ZFiv2FBT6ikXndlty5sw6vMatAhg1v+w3CaaGZOf1CP81jl4Mc8Zrj08A== + dependencies: + "@ethersproject/abi" "5.0.9" + "@ethersproject/abstract-provider" "5.0.7" + "@ethersproject/abstract-signer" "5.0.9" + "@ethersproject/address" "5.0.8" + "@ethersproject/base64" "5.0.6" + "@ethersproject/basex" "5.0.6" + "@ethersproject/bignumber" "5.0.12" + "@ethersproject/bytes" "5.0.8" + "@ethersproject/constants" "5.0.7" + "@ethersproject/contracts" "5.0.8" + "@ethersproject/hash" "5.0.9" + "@ethersproject/hdnode" "5.0.7" + "@ethersproject/json-wallets" "5.0.9" + "@ethersproject/keccak256" "5.0.6" + "@ethersproject/logger" "5.0.8" + "@ethersproject/networks" "5.0.6" + "@ethersproject/pbkdf2" "5.0.6" + "@ethersproject/properties" "5.0.6" + "@ethersproject/providers" "5.0.17" + "@ethersproject/random" "5.0.6" + "@ethersproject/rlp" "5.0.6" + "@ethersproject/sha2" "5.0.6" + "@ethersproject/signing-key" "5.0.7" + "@ethersproject/solidity" "5.0.7" + "@ethersproject/strings" "5.0.7" + "@ethersproject/transactions" "5.0.8" + "@ethersproject/units" "5.0.8" + "@ethersproject/wallet" "5.0.9" + "@ethersproject/web" "5.0.11" + "@ethersproject/wordlists" "5.0.7" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +events@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-package-json@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083" + integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw== + +find-replace@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" + integrity sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A= + dependencies: + array-back "^1.0.4" + test-value "^2.1.0" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-yarn-workspace-root@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" + integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== + dependencies: + fs-extra "^4.0.3" + micromatch "^3.1.4" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + integrity sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA== + dependencies: + is-buffer "~2.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +flow-stoplight@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" + integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s= + +follow-redirects@^1.12.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" + integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== + +for-each@^0.3.3, for-each@~0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^4.0.2, fs-extra@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.0, fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.2.1.tgz#1fb02ded2036a8ac288d507a65962bd87b97628d" + integrity sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA== + +fsevents@~2.1.1, fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +function-bind@^1.1.1, function-bind@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +ganache-core@^2.10.2: + version "2.13.1" + resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.1.tgz#bf60399a2dd084e1090db91cbbc7ed3885dc01e4" + integrity sha512-Ewg+kNcDqXtOohe7jCcP+ZUv9EMzOx2MoqOYYP3BCfxrDh3KjBXXaKK+Let7li0TghAs9lxmBgevZku35j5YzA== + dependencies: + abstract-leveldown "3.0.0" + async "2.6.2" + bip39 "2.5.0" + cachedown "1.0.0" + clone "2.1.2" + debug "3.2.6" + encoding-down "5.0.4" + eth-sig-util "^2.0.0" + ethereumjs-abi "0.6.8" + ethereumjs-account "3.0.0" + ethereumjs-block "2.2.2" + ethereumjs-common "1.5.0" + ethereumjs-tx "2.1.2" + ethereumjs-util "6.2.1" + ethereumjs-vm "4.2.0" + heap "0.2.6" + keccak "3.0.1" + level-sublevel "6.6.4" + levelup "3.1.1" + lodash "4.17.20" + lru-cache "5.1.1" + merkle-patricia-tree "3.0.0" + patch-package "6.2.2" + seedrandom "3.0.1" + source-map-support "0.5.12" + tmp "0.1.0" + web3-provider-engine "14.2.1" + websocket "1.0.32" + optionalDependencies: + ethereumjs-wallet "0.6.5" + web3 "1.2.11" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" + integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2, glob@^7.1.3, glob@~7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hardhat-typechain@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/hardhat-typechain/-/hardhat-typechain-0.3.5.tgz#8e50616a9da348b33bd001168c8fda9c66b7b4af" + integrity sha512-w9lm8sxqTJACY+V7vijiH+NkPExnmtiQEjsV9JKD1KgMdVk2q8y+RhvU/c4B7+7b1+HylRUCxpOIvFuB3rE4+w== + +hardhat@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.2.0.tgz#7d569d29678e5f786a228390b4c0b0cc9fd8ae32" + integrity sha512-3g0qFoQTkR4gfcHDZr59vPfbSH2PiAyxzYblkAAHUNTPBadO5W26z5RWzDv6/lRu8SqZZ9/8AdGZX4IZEa8EDg== + dependencies: + "@ethereumjs/block" "^3.2.1" + "@ethereumjs/blockchain" "^5.2.1" + "@ethereumjs/common" "^2.2.0" + "@ethereumjs/tx" "^3.1.3" + "@ethereumjs/vm" "^5.3.2" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.11.0" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + eth-sig-util "^2.5.2" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.0.10" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "^4.1.0" + mnemonist "^0.38.0" + mocha "^7.1.2" + node-fetch "^2.6.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + uuid "^3.3.2" + ws "^7.2.1" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3, has@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +heap@0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" + integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= + +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +immutable@^4.0.0-rc.12: + version "4.0.0-rc.12" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217" + integrity sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" + integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" + integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + dependencies: + call-bind "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-negative-zero@^2.0.0, is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.4, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-regex@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.1" + +is-regex@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.5.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.5.2, jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" + integrity sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA== + dependencies: + async "^2.0.1" + babel-preset-env "^1.7.0" + babelify "^7.3.0" + json-rpc-error "^2.0.0" + promise-to-callback "^1.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-error@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02" + integrity sha1-p6+cICg4tekFxyUOVH8a/3cligI= + dependencies: + inherits "^2.0.1" + +json-rpc-random-id@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@3.0.1, keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz#ccfff7c046dcf47955ae9a86f46dfa06a31688b4" + integrity sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" + xtend "^4.0.0" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-iterator-stream@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz#2c98a4f8820d87cdacab3132506815419077c730" + integrity sha512-nEIQvxEED9yRThxvOrq8Aqziy4EGzrxSZK+QzEFAVuJvQ8glfyZ96GB6BoI4sBbLfjMXm2w4vu3Tkcm9obcY0g== + dependencies: + inherits "^2.0.1" + readable-stream "^2.3.6" + xtend "^4.0.0" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-mem@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" + integrity sha512-LbtfK9+3Ug1UmvvhR2DqLqXiPW1OJ5jEh0a3m9ZgAipiwpSxGj/qaVVy54RG5vAQN1nCuXqjvprCuKSCxcJHBg== + dependencies: + level-packager "~4.0.0" + memdown "~3.0.0" + +level-mem@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" + integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== + dependencies: + level-packager "^5.0.3" + memdown "^5.0.0" + +level-packager@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-packager@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" + integrity sha512-svCRKfYLn9/4CoFfi+d8krOtrp6RoX8+xm0Na5cgXMqSyRru0AnDYdLl+YI8u1FyS6gGZ94ILLZDE5dh2but3Q== + dependencies: + encoding-down "~5.0.0" + levelup "^3.0.0" + +level-post@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/level-post/-/level-post-1.0.7.tgz#19ccca9441a7cc527879a0635000f06d5e8f27d0" + integrity sha512-PWYqG4Q00asOrLhX7BejSajByB4EmG2GaKHfj3h5UmmZ2duciXLPGYWIjBzLECFWUGOZWlm5B20h/n3Gs3HKew== + dependencies: + ltgt "^2.1.2" + +level-sublevel@6.6.4: + version "6.6.4" + resolved "https://registry.yarnpkg.com/level-sublevel/-/level-sublevel-6.6.4.tgz#f7844ae893919cd9d69ae19d7159499afd5352ba" + integrity sha512-pcCrTUOiO48+Kp6F1+UAzF/OtWqLcQVTVF39HLdZ3RO8XBoXt+XVPKZO1vVr1aUoxHZA9OtD2e1v7G+3S5KFDA== + dependencies: + bytewise "~1.1.0" + level-codec "^9.0.0" + level-errors "^2.0.0" + level-iterator-stream "^2.0.3" + ltgt "~2.1.1" + pull-defer "^0.2.2" + pull-level "^2.0.3" + pull-stream "^3.6.8" + typewiselite "~1.0.0" + xtend "~4.0.0" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +level-ws@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-1.0.0.tgz#19a22d2d4ac57b18cc7c6ecc4bd23d899d8f603b" + integrity sha512-RXEfCmkd6WWFlArh3X8ONvQPm8jNpfA0s/36M4QzLqrLEIt1iJE9WBHLZ5vZJK6haMjJPJGJCQWfjMNnRcq/9Q== + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.8" + xtend "^4.0.1" + +level-ws@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" + integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== + dependencies: + inherits "^2.0.3" + readable-stream "^3.1.0" + xtend "^4.0.1" + +levelup@3.1.1, levelup@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" + integrity sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg== + dependencies: + deferred-leveldown "~4.0.0" + level-errors "~2.0.0" + level-iterator-stream "~3.0.0" + xtend "~4.0.0" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.assign@^4.0.3, lodash.assign@^4.0.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + +lodash@4.17.20, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + +looper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec" + integrity sha1-Zs0Md0rz1P7axTeU90LbVtqPCew= + +looper@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749" + integrity sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k= + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@5.1.1, lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + integrity sha1-cXibO39Tmb7IVl3aOKow0qCX7+4= + dependencies: + pseudomap "^1.0.1" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + +ltgt@^2.1.2, ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +ltgt@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34" + integrity sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ= + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +mcl-wasm@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.6.tgz#c1789ebda5565d49b77d2ee195ff3e4d282f1554" + integrity sha512-cbRl3sUOkBeRY2hsM4t1EIln2TIdQBkSiTOqNTv/4Hu5KOECnMWCgjIf+a9Ebunyn22VKqkMF3zj6ejRzz7YBw== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memdown@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" + integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== + dependencies: + abstract-leveldown "~6.2.1" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.2.0" + +memdown@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" + integrity sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA== + dependencies: + abstract-leveldown "~5.0.0" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merkle-patricia-tree@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" + integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== + dependencies: + async "^2.6.1" + ethereumjs-util "^5.2.0" + level-mem "^3.0.1" + level-ws "^1.0.0" + readable-stream "^3.0.6" + rlp "^2.0.0" + semaphore ">=1.0.1" + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +merkle-patricia-tree@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.1.0.tgz#010636c4cfd68682df33a2e3186b7d0be7b98b9d" + integrity sha512-vmP1J7FwIpprFMVjjSMM1JAwFce85Q+tp0TYIedYv8qaMh2oLUZ3ETXn9wbgi9S6elySzKzGa+Ai6VNKGEwSlg== + dependencies: + "@types/levelup" "^4.3.0" + ethereumjs-util "^7.0.8" + level-mem "^5.0.1" + level-ws "^2.0.0" + readable-stream "^3.6.0" + rlp "^2.2.3" + semaphore-async-await "^1.5.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" + integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== + dependencies: + minimist "^1.2.5" + +mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mnemonist@^0.38.0: + version "0.38.3" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" + integrity sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw== + dependencies: + obliterator "^1.6.1" + +mocha-chai-jest-snapshot@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/mocha-chai-jest-snapshot/-/mocha-chai-jest-snapshot-1.1.1.tgz#7e49f20d0c12e6792d7f7da2e4ee0c38950571cc" + integrity sha512-52GyyqRD/cI8AIiMTQzizKmLeKQvZRBLwWOHwlwytUKPnWSNpRy1MkIcJIlgUrs5ocrjujOWwtKwyVkLWT/DFQ== + dependencies: + "@jest/test-result" "^26.5.2" + chalk "^4.1.0" + find-package-json "^1.2.0" + jest-snapshot "^26.5.2" + jest-util "^26.5.2" + slash "^3.0.0" + yargs "^16.0.3" + +mocha@^6.2.2: + version "6.2.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912" + integrity sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" + minimatch "3.0.4" + mkdirp "0.5.4" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mocha@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mock-fs@^4.1.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598" + integrity sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.0.0, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.8.0, object-inspect@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + +object-inspect@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-is@^1.0.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" + integrity sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.assign@^4.1.1, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz#0dfda8d108074d9c563e80490c883b6661091544" + integrity sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +object.getownpropertydescriptors@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" + integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +obliterator@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3" + integrity sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig== + +oboe@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" + integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" + integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +patch-package@6.2.2, patch-package@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.2.2.tgz#71d170d650c65c26556f0d0fbbb48d92b6cc5f39" + integrity sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^1.2.1" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + +pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.0.0-alpha.59: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.2.tgz#312a429cd0026b2cbdbe0ad8ef30c4f8db1f74b2" + integrity sha512-afn8Q0E0fY2I26fbagiBo1XRe7Cv/vs3t/N5Xbndzjgln+TXrtNxgWzhdZcFoZLN92WrFbxqqDoP6Lk5L80Fmw== + dependencies: + "@solidity-parser/parser" "^0.10.1" + dir-to-object "^2.0.0" + emoji-regex "^9.0.0" + escape-string-regexp "^4.0.0" + prettier "^2.0.5" + semver "^7.3.2" + solidity-comments-extractor "^0.0.4" + string-width "^4.2.0" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.0.5, prettier@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +printj@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pull-cat@^1.1.9: + version "1.1.11" + resolved "https://registry.yarnpkg.com/pull-cat/-/pull-cat-1.1.11.tgz#b642dd1255da376a706b6db4fa962f5fdb74c31b" + integrity sha1-tkLdElXaN2pwa220+pYvX9t0wxs= + +pull-defer@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/pull-defer/-/pull-defer-0.2.3.tgz#4ee09c6d9e227bede9938db80391c3dac489d113" + integrity sha512-/An3KE7mVjZCqNhZsr22k1Tx8MACnUnHZZNPSJ0S62td8JtYr/AiRG42Vz7Syu31SoTLUzVIe61jtT/pNdjVYA== + +pull-level@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pull-level/-/pull-level-2.0.4.tgz#4822e61757c10bdcc7cf4a03af04c92734c9afac" + integrity sha512-fW6pljDeUThpq5KXwKbRG3X7Ogk3vc75d5OQU/TvXXui65ykm+Bn+fiktg+MOx2jJ85cd+sheufPL+rw9QSVZg== + dependencies: + level-post "^1.0.7" + pull-cat "^1.1.9" + pull-live "^1.0.1" + pull-pushable "^2.0.0" + pull-stream "^3.4.0" + pull-window "^2.1.4" + stream-to-pull-stream "^1.7.1" + +pull-live@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pull-live/-/pull-live-1.0.1.tgz#a4ecee01e330155e9124bbbcf4761f21b38f51f5" + integrity sha1-pOzuAeMwFV6RJLu89HYfIbOPUfU= + dependencies: + pull-cat "^1.1.9" + pull-stream "^3.4.0" + +pull-pushable@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pull-pushable/-/pull-pushable-2.2.0.tgz#5f2f3aed47ad86919f01b12a2e99d6f1bd776581" + integrity sha1-Xy867UethpGfAbEqLpnW8b13ZYE= + +pull-stream@^3.2.3, pull-stream@^3.4.0, pull-stream@^3.6.8: + version "3.6.14" + resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.14.tgz#529dbd5b86131f4a5ed636fdf7f6af00781357ee" + integrity sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew== + +pull-window@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/pull-window/-/pull-window-2.1.4.tgz#fc3b86feebd1920c7ae297691e23f705f88552f0" + integrity sha1-/DuG/uvRkgx64pdpHiP3BfiFUvA= + dependencies: + looper "^2.0.0" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@^6.7.0: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-is@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" + integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + +regenerate@^1.2.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q== + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA= + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.17.0, resolve@~1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +resolve@^1.10.0, resolve@^1.18.1, resolve@^1.8.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +resumer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" + integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= + dependencies: + through "~2.3.4" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.2.8, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^6.4.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +scryptsy@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" + integrity sha1-oyJfpLJST4AnAHYeKFW987LZIWM= + dependencies: + pbkdf2 "^3.0.3" + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +seedrandom@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.1.tgz#eb3dde015bcf55df05a233514e5df44ef9dce083" + integrity sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg== + +semaphore-async-await@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" + integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= + +semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solc@^0.4.20: + version "0.4.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.26.tgz#5390a62a99f40806b86258c737c1cf653cc35cb5" + integrity sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA== + dependencies: + fs-extra "^0.30.0" + memorystream "^0.3.1" + require-from-string "^1.1.0" + semver "^5.3.0" + yargs "^4.7.1" + +solc@^0.6.3: + version "0.6.12" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.6.12.tgz#48ac854e0c729361b22a7483645077f58cba080e" + integrity sha512-Lm0Ql2G9Qc7yPP2Ba+WNmzw2jwsrd3u4PobHYlSOxaut3TtUbj9+5ZrT6f4DUpNPEoBaFUOEg9Op9C0mk7ge9g== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== + dependencies: + prettier-linter-helpers "^1.0.0" + +solhint@^3.2.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.2.tgz#ebd7270bb50fd378b427d7a6fc9f2a7fd00216c0" + integrity sha512-8tHCkIAk1axLLG6Qu2WIH3GgNABonj9eAWejJbov3o3ujkZQRNHeHU1cC4/Dmjsh3Om7UzFFeADUHu2i7ZJeiw== + dependencies: + "@solidity-parser/parser" "^0.8.2" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-comments-extractor@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.4.tgz#ce420aef23641ffd0131c7d80ba85b6e1e42147e" + integrity sha512-58glBODwXIKMaQ7rfcJOrWtFQMMOK28tJ0/LcB5Xhu7WtAxk4UX2fpgKPuaL41XjMp/y0gAa1MTLqk018wuSzA== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + +source-map-support@^0.5.13, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== + dependencies: + escape-string-regexp "^2.0.0" + +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-to-pull-stream@^1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz#4161aa2d2eb9964de60bfa1af7feaf917e874ece" + integrity sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg== + dependencies: + looper "^3.0.0" + pull-stream "^3.2.3" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trim@~1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz#d23a22fde01c1e6571a7fadcb9be11decd8061a7" + integrity sha512-16IL9pIBA5asNOSukPfxX2W68BaBvxyiRK16H3RA/lWW9BDosh+w7f+LhomPHpXJ82QEe7w7/rY/S1CV97raLg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +string.prototype.trimend@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@2.0.1, strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tape@^4.6.3: + version "4.13.3" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.3.tgz#51b3d91c83668c7a45b1a594b607dee0a0b46278" + integrity sha512-0/Y20PwRIUkQcTCSi4AASs+OANZZwqPKaipGCEwp10dQMipVvSZwUUCi01Y/OklIGyHKFhIcjock+DKnBfLAFw== + dependencies: + deep-equal "~1.1.1" + defined "~1.0.0" + dotignore "~0.1.2" + for-each "~0.3.3" + function-bind "~1.1.1" + glob "~7.1.6" + has "~1.0.3" + inherits "~2.0.4" + is-regex "~1.0.5" + minimist "~1.2.5" + object-inspect "~1.7.0" + resolve "~1.17.0" + resumer "~0.0.0" + string.prototype.trim "~1.2.1" + through "~2.3.8" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +test-value@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" + integrity sha1-Edpv9nDzRxpztiXKTz/c97t0gpE= + dependencies: + array-back "^1.0.3" + typical "^2.6.0" + +testrpc@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed" + integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6, through@~2.3.4, through@~2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +tmp@0.0.33, tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +"true-case-path@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" + integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== + +ts-essentials@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" + integrity sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ== + +ts-essentials@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.1.tgz#d205508cae0cdadfb73c89503140cf2228389e2d" + integrity sha512-8lwh3QJtIc1UWhkQtr9XuksXu3O0YQdEE5g79guDfhCaU1FWTDIEDZ1ZSx4HTHUmlJZ8L812j3BZQ4a0aOUkSA== + +ts-generator@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ts-generator/-/ts-generator-0.1.1.tgz#af46f2fb88a6db1f9785977e9590e7bcd79220ab" + integrity sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ== + dependencies: + "@types/mkdirp" "^0.5.2" + "@types/prettier" "^2.1.1" + "@types/resolve" "^0.0.8" + chalk "^2.4.1" + glob "^7.1.2" + mkdirp "^0.5.1" + prettier "^2.1.2" + resolve "^1.8.1" + ts-essentials "^1.0.0" + +ts-node@^8.5.4: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +tslib@^1.9.0, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +tweetnacl@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + +typechain@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-4.0.1.tgz#b40eaf5ede15588d97a4b9a5f85120f7ea1cf262" + integrity sha512-H/1VpRmplp1qhCTVLU9PCgzyVCQ7Lth7YvaaI1hTvT31IpWnLLNpDpQD4vXJGr26T9BsZ0ZIceOwieAbcoywXw== + dependencies: + command-line-args "^4.0.7" + debug "^4.1.1" + fs-extra "^7.0.0" + js-sha3 "^0.8.0" + lodash "^4.17.15" + ts-essentials "^7.0.1" + ts-generator "^0.1.1" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@^3.7.3: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + +typewise-core@^1.2, typewise-core@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195" + integrity sha1-l+uRgFx/VdL5QXSPpQ0xXZke8ZU= + +typewise@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typewise/-/typewise-1.0.3.tgz#1067936540af97937cc5dcf9922486e9fa284651" + integrity sha1-EGeTZUCvl5N8xdz5kiSG6fooRlE= + dependencies: + typewise-core "^1.2.0" + +typewiselite@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typewiselite/-/typewiselite-1.0.0.tgz#c8882fa1bb1092c06005a97f34ef5c8508e3664e" + integrity sha1-yIgvobsQksBgBal/NO9chQjjZk4= + +typical@^2.6.0, typical@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" + integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0= + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +underscore@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unorm@^1.3.3: + version "1.6.0" + resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" + integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +utf-8-validate@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.3.tgz#3b64e418ad2ff829809025fdfef595eab2f03a27" + integrity sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A== + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +util.promisify@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" + integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + for-each "^0.3.3" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.1" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +web3-bzz@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.11.tgz#41bc19a77444bd5365744596d778b811880f707f" + integrity sha512-XGpWUEElGypBjeFyUhTkiPXFbDVD6Nr/S5jznE3t8cWUA0FxRf1n3n/NuIZeb0H9RkN2Ctd/jNma/k8XGa3YKg== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.9.1" + +web3-core-helpers@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz#84c681ed0b942c0203f3b324a245a127e8c67a99" + integrity sha512-PEPoAoZd5ME7UfbnCZBdzIerpe74GEvlwT4AjOmHeCVZoIFk7EqvOZDejJHt+feJA6kMVTdd0xzRNN295UhC1A== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.11" + web3-utils "1.2.11" + +web3-core-method@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.11.tgz#f880137d1507a0124912bf052534f168b8d8fbb6" + integrity sha512-ff0q76Cde94HAxLDZ6DbdmKniYCQVtvuaYh+rtOUMB6kssa5FX0q3vPmixi7NPooFnbKmmZCM6NvXg4IreTPIw== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.9.1" + web3-core-helpers "1.2.11" + web3-core-promievent "1.2.11" + web3-core-subscriptions "1.2.11" + web3-utils "1.2.11" + +web3-core-promievent@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz#51fe97ca0ddec2f99bf8c3306a7a8e4b094ea3cf" + integrity sha512-il4McoDa/Ox9Agh4kyfQ8Ak/9ABYpnF8poBLL33R/EnxLsJOGQG2nZhkJa3I067hocrPSjEdlPt/0bHXsln4qA== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz#fe6eb603fbaee18530293a91f8cf26d8ae28c45a" + integrity sha512-oFhBtLfOiIbmfl6T6gYjjj9igOvtyxJ+fjS+byRxiwFJyJ5BQOz4/9/17gWR1Cq74paTlI7vDGxYfuvfE/mKvA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.11" + web3-providers-http "1.2.11" + web3-providers-ipc "1.2.11" + web3-providers-ws "1.2.11" + +web3-core-subscriptions@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz#beca908fbfcb050c16f45f3f0f4c205e8505accd" + integrity sha512-qEF/OVqkCvQ7MPs1JylIZCZkin0aKK9lDxpAtQ1F8niEDGFqn7DT8E/vzbIa0GsOjL2fZjDhWJsaW+BSoAW1gg== + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + +web3-core@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.11.tgz#1043cacc1becb80638453cc5b2a14be9050288a7" + integrity sha512-CN7MEYOY5ryo5iVleIWRE3a3cZqVaLlIbIzDPsvQRUfzYnvzZQRZBm9Mq+ttDi2STOOzc1MKylspz/o3yq/LjQ== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-requestmanager "1.2.11" + web3-utils "1.2.11" + +web3-eth-abi@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz#a887494e5d447c2926d557a3834edd66e17af9b0" + integrity sha512-PkRYc0+MjuLSgg03QVWqWlQivJqRwKItKtEpRUaxUAeLE7i/uU39gmzm2keHGcQXo3POXAbOnMqkDvOep89Crg== + dependencies: + "@ethersproject/abi" "5.0.0-beta.153" + underscore "1.9.1" + web3-utils "1.2.11" + +web3-eth-accounts@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz#a9e3044da442d31903a7ce035a86d8fa33f90520" + integrity sha512-6FwPqEpCfKIh3nSSGeo3uBm2iFSnFJDfwL3oS9pyegRBXNsGRVpgiW63yhNzL0796StsvjHWwQnQHsZNxWAkGw== + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-utils "1.2.11" + +web3-eth-contract@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz#917065902bc27ce89da9a1da26e62ef663663b90" + integrity sha512-MzYuI/Rq2o6gn7vCGcnQgco63isPNK5lMAan2E51AJLknjSLnOxwNY3gM8BcKoy4Z+v5Dv00a03Xuk78JowFow== + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-promievent "1.2.11" + web3-core-subscriptions "1.2.11" + web3-eth-abi "1.2.11" + web3-utils "1.2.11" + +web3-eth-ens@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz#26d4d7f16d6cbcfff918e39832b939edc3162532" + integrity sha512-dbW7dXP6HqT1EAPvnniZVnmw6TmQEKF6/1KgAxbo8iBBYrVTMDGFQUUnZ+C4VETGrwwaqtX4L9d/FrQhZ6SUiA== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-promievent "1.2.11" + web3-eth-abi "1.2.11" + web3-eth-contract "1.2.11" + web3-utils "1.2.11" + +web3-eth-iban@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz#f5f73298305bc7392e2f188bf38a7362b42144ef" + integrity sha512-ozuVlZ5jwFC2hJY4+fH9pIcuH1xP0HEFhtWsR69u9uDIANHLPQQtWYmdj7xQ3p2YT4bQLq/axKhZi7EZVetmxQ== + dependencies: + bn.js "^4.11.9" + web3-utils "1.2.11" + +web3-eth-personal@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz#a38b3942a1d87a62070ce0622a941553c3d5aa70" + integrity sha512-42IzUtKq9iHZ8K9VN0vAI50iSU9tOA1V7XU2BhF/tb7We2iKBVdkley2fg26TxlOcKNEHm7o6HRtiiFsVK4Ifw== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-net "1.2.11" + web3-utils "1.2.11" + +web3-eth@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.11.tgz#4c81fcb6285b8caf544058fba3ae802968fdc793" + integrity sha512-REvxW1wJ58AgHPcXPJOL49d1K/dPmuw4LjPLBPStOVkQjzDTVmJEIsiLwn2YeuNDd4pfakBwT8L3bz1G1/wVsQ== + dependencies: + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-subscriptions "1.2.11" + web3-eth-abi "1.2.11" + web3-eth-accounts "1.2.11" + web3-eth-contract "1.2.11" + web3-eth-ens "1.2.11" + web3-eth-iban "1.2.11" + web3-eth-personal "1.2.11" + web3-net "1.2.11" + web3-utils "1.2.11" + +web3-net@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.11.tgz#eda68ef25e5cdb64c96c39085cdb74669aabbe1b" + integrity sha512-sjrSDj0pTfZouR5BSTItCuZ5K/oZPVdVciPQ6981PPPIwJJkCMeVjD7I4zO3qDPCnBjBSbWvVnLdwqUBPtHxyg== + dependencies: + web3-core "1.2.11" + web3-core-method "1.2.11" + web3-utils "1.2.11" + +web3-provider-engine@14.2.1: + version "14.2.1" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.2.1.tgz#ef351578797bf170e08d529cb5b02f8751329b95" + integrity sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw== + dependencies: + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^3.0.0" + eth-json-rpc-infura "^3.1.0" + eth-sig-util "^1.4.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +web3-providers-http@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.11.tgz#1cd03442c61670572d40e4dcdf1faff8bd91e7c6" + integrity sha512-psh4hYGb1+ijWywfwpB2cvvOIMISlR44F/rJtYkRmQ5jMvG4FOCPlQJPiHQZo+2cc3HbktvvSJzIhkWQJdmvrA== + dependencies: + web3-core-helpers "1.2.11" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz#d16d6c9be1be6e0b4f4536c4acc16b0f4f27ef21" + integrity sha512-yhc7Y/k8hBV/KlELxynWjJDzmgDEDjIjBzXK+e0rHBsYEhdCNdIH5Psa456c+l0qTEU2YzycF8VAjYpWfPnBpQ== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + +web3-providers-ws@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz#a1dfd6d9778d840561d9ec13dd453046451a96bb" + integrity sha512-ZxnjIY1Er8Ty+cE4migzr43zA/+72AF1myzsLaU5eVgdsfV7Jqx7Dix1hbevNZDKFlSoEyq/3j/jYalh3So1Zg== + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + websocket "^1.0.31" + +web3-shh@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.11.tgz#f5d086f9621c9a47e98d438010385b5f059fd88f" + integrity sha512-B3OrO3oG1L+bv3E1sTwCx66injW1A8hhwpknDUbV+sw3fehFazA06z9SGXUefuFI1kVs4q2vRi0n4oCcI4dZDg== + dependencies: + web3-core "1.2.11" + web3-core-method "1.2.11" + web3-core-subscriptions "1.2.11" + web3-net "1.2.11" + +web3-utils@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.11.tgz#af1942aead3fb166ae851a985bed8ef2c2d95a82" + integrity sha512-3Tq09izhD+ThqHEaWYX4VOT7dNPdZiO+c/1QMA0s5X2lDFKK/xHJb7cyTRRVzN2LvlHbR7baS1tmQhSua51TcQ== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@^1.0.0-beta.31: + version "1.3.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.1.tgz#9aa880dd8c9463fe5c099107889f86a085370c2e" + integrity sha512-9gPwFm8SXtIJuzdrZ37PRlalu40fufXxo+H2PiCwaO6RpKGAvlUlWU0qQbyToFNXg7W2H8djEgoAVac8NLMCKQ== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" + integrity sha512-mjQ8HeU41G6hgOYm1pmeH0mRAeNKJGnJEUzDMoerkpw7QUQT4exVREgF1MYPvL/z6vAshOXei25LE/t/Bxl8yQ== + dependencies: + web3-bzz "1.2.11" + web3-core "1.2.11" + web3-eth "1.2.11" + web3-eth-personal "1.2.11" + web3-net "1.2.11" + web3-shh "1.2.11" + web3-utils "1.2.11" + +websocket@1.0.32: + version "1.0.32" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.32.tgz#1f16ddab3a21a2d929dec1687ab21cfdc6d3dbb1" + integrity sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +websocket@^1.0.31: + version "1.0.33" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.33.tgz#407f763fc58e74a3fa41ca3ae5d78d3f5e3b82a5" + integrity sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +ws@^7.2.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb" + integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ== + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@^16.0.3: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^4.7.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.1" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/lib/uniswap/v3-periphery/.gitattributes b/lib/uniswap/v3-periphery/.gitattributes new file mode 100644 index 0000000..7cc88f0 --- /dev/null +++ b/lib/uniswap/v3-periphery/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity \ No newline at end of file diff --git a/lib/uniswap/v3-periphery/.github/stale.yml b/lib/uniswap/v3-periphery/.github/stale.yml new file mode 100644 index 0000000..91b9bf5 --- /dev/null +++ b/lib/uniswap/v3-periphery/.github/stale.yml @@ -0,0 +1,25 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +issues: + # Number of days of inactivity before an Issue or Pull Request becomes stale + daysUntilStale: 7 + + # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. + # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. + daysUntilClose: 7 + + # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) + onlyLabels: + - question + - autoclose + + # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable + exemptLabels: + - p0 + - bug + + # Comment to post when marking as stale. Set to `false` to disable + markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. diff --git a/lib/uniswap/v3-periphery/.github/workflows/lint.yml b/lib/uniswap/v3-periphery/.github/workflows/lint.yml new file mode 100644 index 0000000..2f78834 --- /dev/null +++ b/lib/uniswap/v3-periphery/.github/workflows/lint.yml @@ -0,0 +1,33 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + +jobs: + run-linters: + name: Run linters + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v1 + with: + node-version: 12.x + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run linters + uses: wearerequired/lint-action@a8497ddb33fb1205941fd40452ca9fff07e0770d + with: + github_token: ${{ secrets.github_token }} + prettier: true + auto_fix: true + prettier_extensions: 'css,html,js,json,jsx,md,sass,scss,ts,tsx,vue,yaml,yml,sol' diff --git a/lib/uniswap/v3-periphery/.github/workflows/tests.yml b/lib/uniswap/v3-periphery/.github/workflows/tests.yml new file mode 100644 index 0000000..ba7a04b --- /dev/null +++ b/lib/uniswap/v3-periphery/.github/workflows/tests.yml @@ -0,0 +1,39 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + +jobs: + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 12.x + registry-url: https://registry.npmjs.org + + - id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v1 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- + + - name: Install dependencies + run: yarn install --frozen-lockfile + + # This is required separately from yarn test because it generates the typechain definitions + - name: Compile + run: yarn compile + + - name: Run unit tests + run: yarn test diff --git a/lib/uniswap/v3-periphery/.gitignore b/lib/uniswap/v3-periphery/.gitignore new file mode 100644 index 0000000..2be090a --- /dev/null +++ b/lib/uniswap/v3-periphery/.gitignore @@ -0,0 +1,5 @@ +artifacts/ +cache/ +crytic-export/ +node_modules/ +typechain/ \ No newline at end of file diff --git a/lib/uniswap/v3-periphery/.prettierignore b/lib/uniswap/v3-periphery/.prettierignore new file mode 100644 index 0000000..821c19d --- /dev/null +++ b/lib/uniswap/v3-periphery/.prettierignore @@ -0,0 +1 @@ +.github \ No newline at end of file diff --git a/lib/uniswap/v3-periphery/.prettierrc b/lib/uniswap/v3-periphery/.prettierrc new file mode 100644 index 0000000..31ba22d --- /dev/null +++ b/lib/uniswap/v3-periphery/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "printWidth": 120 +} diff --git a/lib/uniswap/v3-periphery/.solhint.json b/lib/uniswap/v3-periphery/.solhint.json new file mode 100644 index 0000000..11b3647 --- /dev/null +++ b/lib/uniswap/v3-periphery/.solhint.json @@ -0,0 +1,6 @@ +{ + "plugins": ["prettier"], + "rules": { + "prettier/prettier": "error" + } +} diff --git a/lib/uniswap/v3-periphery/.yarnrc b/lib/uniswap/v3-periphery/.yarnrc new file mode 100644 index 0000000..5455c6c --- /dev/null +++ b/lib/uniswap/v3-periphery/.yarnrc @@ -0,0 +1 @@ +ignore-scripts true diff --git a/lib/uniswap/v3-periphery/LICENSE b/lib/uniswap/v3-periphery/LICENSE new file mode 100644 index 0000000..ecbc059 --- /dev/null +++ b/lib/uniswap/v3-periphery/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. \ No newline at end of file diff --git a/lib/uniswap/v3-periphery/README.md b/lib/uniswap/v3-periphery/README.md new file mode 100644 index 0000000..8d7345b --- /dev/null +++ b/lib/uniswap/v3-periphery/README.md @@ -0,0 +1,52 @@ +# Uniswap V3 Periphery + +[![Tests](https://github.com/Uniswap/uniswap-v3-periphery/workflows/Tests/badge.svg)](https://github.com/Uniswap/uniswap-v3-periphery/actions?query=workflow%3ATests) +[![Lint](https://github.com/Uniswap/uniswap-v3-periphery/workflows/Lint/badge.svg)](https://github.com/Uniswap/uniswap-v3-periphery/actions?query=workflow%3ALint) + +This repository contains the periphery smart contracts for the Uniswap V3 Protocol. +For the lower level core contracts, see the [uniswap-v3-core](https://github.com/Uniswap/uniswap-v3-core) +repository. + +## Bug bounty + +This repository is subject to the Uniswap V3 bug bounty program, +per the terms defined [here](./bug-bounty.md). + +## Local deployment + +In order to deploy this code to a local testnet, you should install the npm package +`@uniswap/v3-periphery` +and import bytecode imported from artifacts located at +`@uniswap/v3-periphery/artifacts/contracts/*/*.json`. +For example: + +```typescript +import { + abi as SWAP_ROUTER_ABI, + bytecode as SWAP_ROUTER_BYTECODE, +} from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json' + +// deploy the bytecode +``` + +This will ensure that you are testing against the same bytecode that is deployed to +mainnet and public testnets, and all Uniswap code will correctly interoperate with +your local deployment. + +## Using solidity interfaces + +The Uniswap v3 periphery interfaces are available for import into solidity smart contracts +via the npm artifact `@uniswap/v3-periphery`, e.g.: + +```solidity +import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol'; + +contract MyContract { + ISwapRouter router; + + function doSomethingWithSwapRouter() { + // router.exactInput(...); + } +} + +``` diff --git a/lib/uniswap/v3-periphery/audits/abdk/audit.pdf b/lib/uniswap/v3-periphery/audits/abdk/audit.pdf new file mode 100644 index 0000000..3608934 Binary files /dev/null and b/lib/uniswap/v3-periphery/audits/abdk/audit.pdf differ diff --git a/lib/uniswap/v3-periphery/bug-bounty.md b/lib/uniswap/v3-periphery/bug-bounty.md new file mode 100644 index 0000000..2fbc14f --- /dev/null +++ b/lib/uniswap/v3-periphery/bug-bounty.md @@ -0,0 +1,80 @@ +# Uniswap V3 Bug Bounty + +## Overview + +Starting on April 26th, 2021, the [uniswap-v3-periphery](https://github.com/Uniswap/uniswap-v3-periphery) repository is +subject to the Uniswap V3 Bug Bounty (the “Program”) to incentivize responsible bug disclosure. + +We are limiting the scope of the Program to critical and high severity bugs, and are offering a reward of up to $500,000. Happy hunting! + +## Scope + +The scope of the Program is limited to bugs that result in the loss of user funds. + +The following are not within the scope of the Program: + +- Any contract located under [contracts/test](./contracts/test) or [contracts/lens](./contracts/lens). +- Bugs in any third party contract or platform that interacts with Uniswap V3. +- Vulnerabilities already reported and/or discovered in contracts built by third parties on Uniswap V3. +- Any already-reported bugs. + +Vulnerabilities contingent upon the occurrence of any of the following also are outside the scope of this Program: + +- Frontend bugs +- DDOS attacks +- Spamming +- Phishing +- Automated tools (Github Actions, AWS, etc.) +- Compromise or misuse of third party systems or services + +## Assumptions + +Uniswap V3 was developed with the following assumptions, and thus any bug must also adhere to the following assumptions +to be eligible for the bug bounty: + +- The total supply of any token does not exceed 2128 - 1, i.e. `type(uint128).max`. +- The `transfer` and `transferFrom` methods of any token strictly decrease the balance of the token sender by the transfer amount and increases the balance of token recipient by the transfer amount, i.e. fee on transfer tokens are excluded. +- The token balance of an address can only change due to a call to `transfer` by the sender or `transferFrom` by an approved address, i.e. rebase tokens and interest bearing tokens are excluded. + +## Rewards + +Rewards will be allocated based on the severity of the bug disclosed and will be evaluated and rewarded at the discretion of the Uniswap Labs team. +For critical bugs that lead to loss of user funds (more than 1% or user specified slippage tolerance), +rewards of up to $500,000 will be granted. Lower severity bugs will be rewarded at the discretion of the team. +In addition, all vulnerabilities disclosed prior to the mainnet launch date will be subject to receive higher rewards. + +## Disclosure + +Any vulnerability or bug discovered must be reported only to the following email: [security@uniswap.org](mailto:security@uniswap.org). + +The vulnerability must not be disclosed publicly or to any other person, entity or email address before Uniswap Labs has been notified, has fixed the issue, and has granted permission for public disclosure. In addition, disclosure must be made within 24 hours following discovery of the vulnerability. + +A detailed report of a vulnerability increases the likelihood of a reward and may increase the reward amount. Please provide as much information about the vulnerability as possible, including: + +- The conditions on which reproducing the bug is contingent. +- The steps needed to reproduce the bug or, preferably, a proof of concept. +- The potential implications of the vulnerability being abused. + +Anyone who reports a unique, previously-unreported vulnerability that results in a change to the code or a configuration change and who keeps such vulnerability confidential until it has been resolved by our engineers will be recognized publicly for their contribution if they so choose. + +## Eligibility + +To be eligible for a reward under this Program, you must: + +- Discover a previously unreported, non-public vulnerability that would result in a loss of and/or lock on any ERC-20 token on Uniswap V3 (but not on any third party platform interacting with Uniswap V3) and that is within the scope of this Program. Vulnerabilities must be distinct from the issues covered in the Trail of Bits or ABDK audits. +- Be the first to disclose the unique vulnerability to [security@uniswap.org](mailto:security@uniswap.org), in compliance with the disclosure requirements above. If similar vulnerabilities are reported within the same 24 hour period, rewards will be split at the discretion of Uniswap Labs. +- Provide sufficient information to enable our engineers to reproduce and fix the vulnerability. +- Not engage in any unlawful conduct when disclosing the bug, including through threats, demands, or any other coercive tactics. +- Not exploit the vulnerability in any way, including through making it public or by obtaining a profit (other than a reward under this Program). +- Make a good faith effort to avoid privacy violations, destruction of data, interruption or degradation of Uniswap V3. +- Submit only one vulnerability per submission, unless you need to chain vulnerabilities to provide impact regarding any of the vulnerabilities. +- Not submit a vulnerability caused by an underlying issue that is the same as an issue on which a reward has been paid under this Program. +- Not be one of our current or former employees, vendors, or contractors or an employee of any of those vendors or contractors. +- Not be subject to US sanctions or reside in a US-embargoed country. +- Be at least 18 years of age or, if younger, submit your vulnerability with the consent of your parent or guardian. + +## Other Terms + +By submitting your report, you grant Uniswap Labs any and all rights, including intellectual property rights, needed to validate, mitigate, and disclose the vulnerability. All reward decisions, including eligibility for and amounts of the rewards and the manner in which such rewards will be paid, are made at our sole discretion. + +The terms and conditions of this Program may be altered at any time. diff --git a/lib/uniswap/v3-periphery/contracts/NonfungiblePositionManager.sol b/lib/uniswap/v3-periphery/contracts/NonfungiblePositionManager.sol new file mode 100644 index 0000000..68532a0 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/NonfungiblePositionManager.sol @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@uniswap/v3-core/contracts/libraries/FixedPoint128.sol'; +import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; + +import './interfaces/INonfungiblePositionManager.sol'; +import './interfaces/INonfungibleTokenPositionDescriptor.sol'; +import './libraries/PositionKey.sol'; +import './libraries/PoolAddress.sol'; +import './base/LiquidityManagement.sol'; +import './base/PeripheryImmutableState.sol'; +import './base/Multicall.sol'; +import './base/ERC721Permit.sol'; +import './base/PeripheryValidation.sol'; +import './base/SelfPermit.sol'; +import './base/PoolInitializer.sol'; + +/// @title NFT positions +/// @notice Wraps Uniswap V3 positions in the ERC721 non-fungible token interface +contract NonfungiblePositionManager is + INonfungiblePositionManager, + Multicall, + ERC721Permit, + PeripheryImmutableState, + PoolInitializer, + LiquidityManagement, + PeripheryValidation, + SelfPermit +{ + // details about the uniswap position + struct Position { + // the nonce for permits + uint96 nonce; + // the address that is approved for spending this token + address operator; + // the ID of the pool with which this token is connected + uint80 poolId; + // the tick range of the position + int24 tickLower; + int24 tickUpper; + // the liquidity of the position + uint128 liquidity; + // the fee growth of the aggregate position as of the last action on the individual position + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + // how many uncollected tokens are owed to the position, as of the last computation + uint128 tokensOwed0; + uint128 tokensOwed1; + } + + /// @dev IDs of pools assigned by this contract + mapping(address => uint80) private _poolIds; + + /// @dev Pool keys by pool ID, to save on SSTOREs for position data + mapping(uint80 => PoolAddress.PoolKey) private _poolIdToPoolKey; + + /// @dev The token ID position data + mapping(uint256 => Position) private _positions; + + /// @dev The ID of the next token that will be minted. Skips 0 + uint176 private _nextId = 1; + /// @dev The ID of the next pool that is used for the first time. Skips 0 + uint80 private _nextPoolId = 1; + + /// @dev The address of the token descriptor contract, which handles generating token URIs for position tokens + address private immutable _tokenDescriptor; + + constructor( + address _factory, + address _WETH9, + address _tokenDescriptor_ + ) ERC721Permit('Uniswap V3 Positions NFT-V1', 'UNI-V3-POS', '1') PeripheryImmutableState(_factory, _WETH9) { + _tokenDescriptor = _tokenDescriptor_; + } + + /// @inheritdoc INonfungiblePositionManager + function positions(uint256 tokenId) + external + view + override + returns ( + uint96 nonce, + address operator, + address token0, + address token1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ) + { + Position memory position = _positions[tokenId]; + require(position.poolId != 0, 'Invalid token ID'); + PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId]; + return ( + position.nonce, + position.operator, + poolKey.token0, + poolKey.token1, + poolKey.fee, + position.tickLower, + position.tickUpper, + position.liquidity, + position.feeGrowthInside0LastX128, + position.feeGrowthInside1LastX128, + position.tokensOwed0, + position.tokensOwed1 + ); + } + + /// @dev Caches a pool key + function cachePoolKey(address pool, PoolAddress.PoolKey memory poolKey) private returns (uint80 poolId) { + poolId = _poolIds[pool]; + if (poolId == 0) { + _poolIds[pool] = (poolId = _nextPoolId++); + _poolIdToPoolKey[poolId] = poolKey; + } + } + + /// @inheritdoc INonfungiblePositionManager + function mint(MintParams calldata params) + external + payable + override + checkDeadline(params.deadline) + returns ( + uint256 tokenId, + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ) + { + IUniswapV3Pool pool; + (liquidity, amount0, amount1, pool) = addLiquidity( + AddLiquidityParams({ + token0: params.token0, + token1: params.token1, + fee: params.fee, + recipient: address(this), + tickLower: params.tickLower, + tickUpper: params.tickUpper, + amount0Desired: params.amount0Desired, + amount1Desired: params.amount1Desired, + amount0Min: params.amount0Min, + amount1Min: params.amount1Min + }) + ); + + _mint(params.recipient, (tokenId = _nextId++)); + + bytes32 positionKey = PositionKey.compute(address(this), params.tickLower, params.tickUpper); + (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey); + + // idempotent set + uint80 poolId = + cachePoolKey( + address(pool), + PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}) + ); + + _positions[tokenId] = Position({ + nonce: 0, + operator: address(0), + poolId: poolId, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + liquidity: liquidity, + feeGrowthInside0LastX128: feeGrowthInside0LastX128, + feeGrowthInside1LastX128: feeGrowthInside1LastX128, + tokensOwed0: 0, + tokensOwed1: 0 + }); + + emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1); + } + + modifier isAuthorizedForToken(uint256 tokenId) { + require(_isApprovedOrOwner(msg.sender, tokenId), 'Not approved'); + _; + } + + function tokenURI(uint256 tokenId) public view override(ERC721, IERC721Metadata) returns (string memory) { + require(_exists(tokenId)); + return INonfungibleTokenPositionDescriptor(_tokenDescriptor).tokenURI(this, tokenId); + } + + // save bytecode by removing implementation of unused method + function baseURI() public pure override returns (string memory) {} + + /// @inheritdoc INonfungiblePositionManager + function increaseLiquidity(IncreaseLiquidityParams calldata params) + external + payable + override + checkDeadline(params.deadline) + returns ( + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ) + { + Position storage position = _positions[params.tokenId]; + + PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId]; + + IUniswapV3Pool pool; + (liquidity, amount0, amount1, pool) = addLiquidity( + AddLiquidityParams({ + token0: poolKey.token0, + token1: poolKey.token1, + fee: poolKey.fee, + tickLower: position.tickLower, + tickUpper: position.tickUpper, + amount0Desired: params.amount0Desired, + amount1Desired: params.amount1Desired, + amount0Min: params.amount0Min, + amount1Min: params.amount1Min, + recipient: address(this) + }) + ); + + bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper); + + // this is now updated to the current transaction + (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey); + + position.tokensOwed0 += uint128( + FullMath.mulDiv( + feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, + position.liquidity, + FixedPoint128.Q128 + ) + ); + position.tokensOwed1 += uint128( + FullMath.mulDiv( + feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128, + position.liquidity, + FixedPoint128.Q128 + ) + ); + + position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128; + position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128; + position.liquidity += liquidity; + + emit IncreaseLiquidity(params.tokenId, liquidity, amount0, amount1); + } + + /// @inheritdoc INonfungiblePositionManager + function decreaseLiquidity(DecreaseLiquidityParams calldata params) + external + payable + override + isAuthorizedForToken(params.tokenId) + checkDeadline(params.deadline) + returns (uint256 amount0, uint256 amount1) + { + require(params.liquidity > 0); + Position storage position = _positions[params.tokenId]; + + uint128 positionLiquidity = position.liquidity; + require(positionLiquidity >= params.liquidity); + + PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId]; + IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + (amount0, amount1) = pool.burn(position.tickLower, position.tickUpper, params.liquidity); + + require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check'); + + bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper); + // this is now updated to the current transaction + (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey); + + position.tokensOwed0 += + uint128(amount0) + + uint128( + FullMath.mulDiv( + feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, + positionLiquidity, + FixedPoint128.Q128 + ) + ); + position.tokensOwed1 += + uint128(amount1) + + uint128( + FullMath.mulDiv( + feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128, + positionLiquidity, + FixedPoint128.Q128 + ) + ); + + position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128; + position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128; + // subtraction is safe because we checked positionLiquidity is gte params.liquidity + position.liquidity = positionLiquidity - params.liquidity; + + emit DecreaseLiquidity(params.tokenId, params.liquidity, amount0, amount1); + } + + /// @inheritdoc INonfungiblePositionManager + function collect(CollectParams calldata params) + external + payable + override + isAuthorizedForToken(params.tokenId) + returns (uint256 amount0, uint256 amount1) + { + require(params.amount0Max > 0 || params.amount1Max > 0); + // allow collecting to the nft position manager address with address 0 + address recipient = params.recipient == address(0) ? address(this) : params.recipient; + + Position storage position = _positions[params.tokenId]; + + PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId]; + + IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + + (uint128 tokensOwed0, uint128 tokensOwed1) = (position.tokensOwed0, position.tokensOwed1); + + // trigger an update of the position fees owed and fee growth snapshots if it has any liquidity + if (position.liquidity > 0) { + pool.burn(position.tickLower, position.tickUpper, 0); + (, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = + pool.positions(PositionKey.compute(address(this), position.tickLower, position.tickUpper)); + + tokensOwed0 += uint128( + FullMath.mulDiv( + feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128, + position.liquidity, + FixedPoint128.Q128 + ) + ); + tokensOwed1 += uint128( + FullMath.mulDiv( + feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128, + position.liquidity, + FixedPoint128.Q128 + ) + ); + + position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128; + position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128; + } + + // compute the arguments to give to the pool#collect method + (uint128 amount0Collect, uint128 amount1Collect) = + ( + params.amount0Max > tokensOwed0 ? tokensOwed0 : params.amount0Max, + params.amount1Max > tokensOwed1 ? tokensOwed1 : params.amount1Max + ); + + // the actual amounts collected are returned + (amount0, amount1) = pool.collect( + recipient, + position.tickLower, + position.tickUpper, + amount0Collect, + amount1Collect + ); + + // sometimes there will be a few less wei than expected due to rounding down in core, but we just subtract the full amount expected + // instead of the actual amount so we can burn the token + (position.tokensOwed0, position.tokensOwed1) = (tokensOwed0 - amount0Collect, tokensOwed1 - amount1Collect); + + emit Collect(params.tokenId, recipient, amount0Collect, amount1Collect); + } + + /// @inheritdoc INonfungiblePositionManager + function burn(uint256 tokenId) external payable override isAuthorizedForToken(tokenId) { + Position storage position = _positions[tokenId]; + require(position.liquidity == 0 && position.tokensOwed0 == 0 && position.tokensOwed1 == 0, 'Not cleared'); + delete _positions[tokenId]; + _burn(tokenId); + } + + function _getAndIncrementNonce(uint256 tokenId) internal override returns (uint256) { + return uint256(_positions[tokenId].nonce++); + } + + /// @inheritdoc IERC721 + function getApproved(uint256 tokenId) public view override(ERC721, IERC721) returns (address) { + require(_exists(tokenId), 'ERC721: approved query for nonexistent token'); + + return _positions[tokenId].operator; + } + + /// @dev Overrides _approve to use the operator in the position, which is packed with the position permit nonce + function _approve(address to, uint256 tokenId) internal override(ERC721) { + _positions[tokenId].operator = to; + emit Approval(ownerOf(tokenId), to, tokenId); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/NonfungibleTokenPositionDescriptor.sol b/lib/uniswap/v3-periphery/contracts/NonfungibleTokenPositionDescriptor.sol new file mode 100644 index 0000000..a8c1628 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/NonfungibleTokenPositionDescriptor.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@uniswap/lib/contracts/libraries/SafeERC20Namer.sol'; + +import './libraries/ChainId.sol'; +import './interfaces/INonfungiblePositionManager.sol'; +import './interfaces/INonfungibleTokenPositionDescriptor.sol'; +import './interfaces/IERC20Metadata.sol'; +import './libraries/PoolAddress.sol'; +import './libraries/NFTDescriptor.sol'; +import './libraries/TokenRatioSortOrder.sol'; + +/// @title Describes NFT token positions +/// @notice Produces a string containing the data URI for a JSON metadata string +contract NonfungibleTokenPositionDescriptor is INonfungibleTokenPositionDescriptor { + address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; + address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + + address public immutable WETH9; + /// @dev A null-terminated string + bytes32 public immutable nativeCurrencyLabelBytes; + + constructor(address _WETH9, bytes32 _nativeCurrencyLabelBytes) { + WETH9 = _WETH9; + nativeCurrencyLabelBytes = _nativeCurrencyLabelBytes; + } + + /// @notice Returns the native currency label as a string + function nativeCurrencyLabel() public view returns (string memory) { + uint256 len = 0; + while (len < 32 && nativeCurrencyLabelBytes[len] != 0) { + len++; + } + bytes memory b = new bytes(len); + for (uint256 i = 0; i < len; i++) { + b[i] = nativeCurrencyLabelBytes[i]; + } + return string(b); + } + + /// @inheritdoc INonfungibleTokenPositionDescriptor + function tokenURI(INonfungiblePositionManager positionManager, uint256 tokenId) + external + view + override + returns (string memory) + { + (, , address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, , , , , ) = + positionManager.positions(tokenId); + + IUniswapV3Pool pool = + IUniswapV3Pool( + PoolAddress.computeAddress( + positionManager.factory(), + PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee}) + ) + ); + + bool _flipRatio = flipRatio(token0, token1, ChainId.get()); + address quoteTokenAddress = !_flipRatio ? token1 : token0; + address baseTokenAddress = !_flipRatio ? token0 : token1; + (, int24 tick, , , , , ) = pool.slot0(); + + return + NFTDescriptor.constructTokenURI( + NFTDescriptor.ConstructTokenURIParams({ + tokenId: tokenId, + quoteTokenAddress: quoteTokenAddress, + baseTokenAddress: baseTokenAddress, + quoteTokenSymbol: quoteTokenAddress == WETH9 + ? nativeCurrencyLabel() + : SafeERC20Namer.tokenSymbol(quoteTokenAddress), + baseTokenSymbol: baseTokenAddress == WETH9 + ? nativeCurrencyLabel() + : SafeERC20Namer.tokenSymbol(baseTokenAddress), + quoteTokenDecimals: IERC20Metadata(quoteTokenAddress).decimals(), + baseTokenDecimals: IERC20Metadata(baseTokenAddress).decimals(), + flipRatio: _flipRatio, + tickLower: tickLower, + tickUpper: tickUpper, + tickCurrent: tick, + tickSpacing: pool.tickSpacing(), + fee: fee, + poolAddress: address(pool) + }) + ); + } + + function flipRatio( + address token0, + address token1, + uint256 chainId + ) public view returns (bool) { + return tokenRatioPriority(token0, chainId) > tokenRatioPriority(token1, chainId); + } + + function tokenRatioPriority(address token, uint256 chainId) public view returns (int256) { + if (token == WETH9) { + return TokenRatioSortOrder.DENOMINATOR; + } + if (chainId == 1) { + if (token == USDC) { + return TokenRatioSortOrder.NUMERATOR_MOST; + } else if (token == USDT) { + return TokenRatioSortOrder.NUMERATOR_MORE; + } else if (token == DAI) { + return TokenRatioSortOrder.NUMERATOR; + } else if (token == TBTC) { + return TokenRatioSortOrder.DENOMINATOR_MORE; + } else if (token == WBTC) { + return TokenRatioSortOrder.DENOMINATOR_MOST; + } else { + return 0; + } + } + return 0; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/SwapRouter.sol b/lib/uniswap/v3-periphery/contracts/SwapRouter.sol new file mode 100644 index 0000000..0b14d56 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/SwapRouter.sol @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/libraries/SafeCast.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +import './interfaces/ISwapRouter.sol'; +import './base/PeripheryImmutableState.sol'; +import './base/PeripheryValidation.sol'; +import './base/PeripheryPaymentsWithFee.sol'; +import './base/Multicall.sol'; +import './base/SelfPermit.sol'; +import './libraries/Path.sol'; +import './libraries/PoolAddress.sol'; +import './libraries/CallbackValidation.sol'; +import './interfaces/external/IWETH9.sol'; + +/// @title Uniswap V3 Swap Router +/// @notice Router for stateless execution of swaps against Uniswap V3 +contract SwapRouter is + ISwapRouter, + PeripheryImmutableState, + PeripheryValidation, + PeripheryPaymentsWithFee, + Multicall, + SelfPermit +{ + using Path for bytes; + using SafeCast for uint256; + + /// @dev Used as the placeholder value for amountInCached, because the computed amount in for an exact output swap + /// can never actually be this value + uint256 private constant DEFAULT_AMOUNT_IN_CACHED = type(uint256).max; + + /// @dev Transient storage variable used for returning the computed amount in for an exact output swap. + uint256 private amountInCached = DEFAULT_AMOUNT_IN_CACHED; + + constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} + + /// @dev Returns the pool for the given token pair and fee. The pool contract may or may not exist. + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) private view returns (IUniswapV3Pool) { + return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee))); + } + + struct SwapCallbackData { + bytes path; + address payer; + } + + /// @inheritdoc IUniswapV3SwapCallback + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata _data + ) external override { + require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported + SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData)); + (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool(); + CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); + + (bool isExactInput, uint256 amountToPay) = + amount0Delta > 0 + ? (tokenIn < tokenOut, uint256(amount0Delta)) + : (tokenOut < tokenIn, uint256(amount1Delta)); + if (isExactInput) { + pay(tokenIn, data.payer, msg.sender, amountToPay); + } else { + // either initiate the next swap or pay + if (data.path.hasMultiplePools()) { + data.path = data.path.skipToken(); + exactOutputInternal(amountToPay, msg.sender, 0, data); + } else { + amountInCached = amountToPay; + tokenIn = tokenOut; // swap in/out because exact output swaps are reversed + pay(tokenIn, data.payer, msg.sender, amountToPay); + } + } + } + + /// @dev Performs a single exact input swap + function exactInputInternal( + uint256 amountIn, + address recipient, + uint160 sqrtPriceLimitX96, + SwapCallbackData memory data + ) private returns (uint256 amountOut) { + // allow swapping to the router address with address 0 + if (recipient == address(0)) recipient = address(this); + + (address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool(); + + bool zeroForOne = tokenIn < tokenOut; + + (int256 amount0, int256 amount1) = + getPool(tokenIn, tokenOut, fee).swap( + recipient, + zeroForOne, + amountIn.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encode(data) + ); + + return uint256(-(zeroForOne ? amount1 : amount0)); + } + + /// @inheritdoc ISwapRouter + function exactInputSingle(ExactInputSingleParams calldata params) + external + payable + override + checkDeadline(params.deadline) + returns (uint256 amountOut) + { + amountOut = exactInputInternal( + params.amountIn, + params.recipient, + params.sqrtPriceLimitX96, + SwapCallbackData({path: abi.encodePacked(params.tokenIn, params.fee, params.tokenOut), payer: msg.sender}) + ); + require(amountOut >= params.amountOutMinimum, 'Too little received'); + } + + /// @inheritdoc ISwapRouter + function exactInput(ExactInputParams memory params) + external + payable + override + checkDeadline(params.deadline) + returns (uint256 amountOut) + { + address payer = msg.sender; // msg.sender pays for the first hop + + while (true) { + bool hasMultiplePools = params.path.hasMultiplePools(); + + // the outputs of prior swaps become the inputs to subsequent ones + params.amountIn = exactInputInternal( + params.amountIn, + hasMultiplePools ? address(this) : params.recipient, // for intermediate swaps, this contract custodies + 0, + SwapCallbackData({ + path: params.path.getFirstPool(), // only the first pool in the path is necessary + payer: payer + }) + ); + + // decide whether to continue or terminate + if (hasMultiplePools) { + payer = address(this); // at this point, the caller has paid + params.path = params.path.skipToken(); + } else { + amountOut = params.amountIn; + break; + } + } + + require(amountOut >= params.amountOutMinimum, 'Too little received'); + } + + /// @dev Performs a single exact output swap + function exactOutputInternal( + uint256 amountOut, + address recipient, + uint160 sqrtPriceLimitX96, + SwapCallbackData memory data + ) private returns (uint256 amountIn) { + // allow swapping to the router address with address 0 + if (recipient == address(0)) recipient = address(this); + + (address tokenOut, address tokenIn, uint24 fee) = data.path.decodeFirstPool(); + + bool zeroForOne = tokenIn < tokenOut; + + (int256 amount0Delta, int256 amount1Delta) = + getPool(tokenIn, tokenOut, fee).swap( + recipient, + zeroForOne, + -amountOut.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encode(data) + ); + + uint256 amountOutReceived; + (amountIn, amountOutReceived) = zeroForOne + ? (uint256(amount0Delta), uint256(-amount1Delta)) + : (uint256(amount1Delta), uint256(-amount0Delta)); + // it's technically possible to not receive the full output amount, + // so if no price limit has been specified, require this possibility away + if (sqrtPriceLimitX96 == 0) require(amountOutReceived == amountOut); + } + + /// @inheritdoc ISwapRouter + function exactOutputSingle(ExactOutputSingleParams calldata params) + external + payable + override + checkDeadline(params.deadline) + returns (uint256 amountIn) + { + // avoid an SLOAD by using the swap return data + amountIn = exactOutputInternal( + params.amountOut, + params.recipient, + params.sqrtPriceLimitX96, + SwapCallbackData({path: abi.encodePacked(params.tokenOut, params.fee, params.tokenIn), payer: msg.sender}) + ); + + require(amountIn <= params.amountInMaximum, 'Too much requested'); + // has to be reset even though we don't use it in the single hop case + amountInCached = DEFAULT_AMOUNT_IN_CACHED; + } + + /// @inheritdoc ISwapRouter + function exactOutput(ExactOutputParams calldata params) + external + payable + override + checkDeadline(params.deadline) + returns (uint256 amountIn) + { + // it's okay that the payer is fixed to msg.sender here, as they're only paying for the "final" exact output + // swap, which happens first, and subsequent swaps are paid for within nested callback frames + exactOutputInternal( + params.amountOut, + params.recipient, + 0, + SwapCallbackData({path: params.path, payer: msg.sender}) + ); + + amountIn = amountInCached; + require(amountIn <= params.amountInMaximum, 'Too much requested'); + amountInCached = DEFAULT_AMOUNT_IN_CACHED; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/V3Migrator.sol b/lib/uniswap/v3-periphery/contracts/V3Migrator.sol new file mode 100644 index 0000000..e0376e2 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/V3Migrator.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol'; +import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; + +import './interfaces/INonfungiblePositionManager.sol'; + +import './libraries/TransferHelper.sol'; + +import './interfaces/IV3Migrator.sol'; +import './base/PeripheryImmutableState.sol'; +import './base/Multicall.sol'; +import './base/SelfPermit.sol'; +import './interfaces/external/IWETH9.sol'; +import './base/PoolInitializer.sol'; + +/// @title Uniswap V3 Migrator +contract V3Migrator is IV3Migrator, PeripheryImmutableState, PoolInitializer, Multicall, SelfPermit { + using LowGasSafeMath for uint256; + + address public immutable nonfungiblePositionManager; + + constructor( + address _factory, + address _WETH9, + address _nonfungiblePositionManager + ) PeripheryImmutableState(_factory, _WETH9) { + nonfungiblePositionManager = _nonfungiblePositionManager; + } + + receive() external payable { + require(msg.sender == WETH9, 'Not WETH9'); + } + + function migrate(MigrateParams calldata params) external override { + require(params.percentageToMigrate > 0, 'Percentage too small'); + require(params.percentageToMigrate <= 100, 'Percentage too large'); + + // burn v2 liquidity to this address + IUniswapV2Pair(params.pair).transferFrom(msg.sender, params.pair, params.liquidityToMigrate); + (uint256 amount0V2, uint256 amount1V2) = IUniswapV2Pair(params.pair).burn(address(this)); + + // calculate the amounts to migrate to v3 + uint256 amount0V2ToMigrate = amount0V2.mul(params.percentageToMigrate) / 100; + uint256 amount1V2ToMigrate = amount1V2.mul(params.percentageToMigrate) / 100; + + // approve the position manager up to the maximum token amounts + TransferHelper.safeApprove(params.token0, nonfungiblePositionManager, amount0V2ToMigrate); + TransferHelper.safeApprove(params.token1, nonfungiblePositionManager, amount1V2ToMigrate); + + // mint v3 position + (, , uint256 amount0V3, uint256 amount1V3) = + INonfungiblePositionManager(nonfungiblePositionManager).mint( + INonfungiblePositionManager.MintParams({ + token0: params.token0, + token1: params.token1, + fee: params.fee, + tickLower: params.tickLower, + tickUpper: params.tickUpper, + amount0Desired: amount0V2ToMigrate, + amount1Desired: amount1V2ToMigrate, + amount0Min: params.amount0Min, + amount1Min: params.amount1Min, + recipient: params.recipient, + deadline: params.deadline + }) + ); + + // if necessary, clear allowance and refund dust + if (amount0V3 < amount0V2) { + if (amount0V3 < amount0V2ToMigrate) { + TransferHelper.safeApprove(params.token0, nonfungiblePositionManager, 0); + } + + uint256 refund0 = amount0V2 - amount0V3; + if (params.refundAsETH && params.token0 == WETH9) { + IWETH9(WETH9).withdraw(refund0); + TransferHelper.safeTransferETH(msg.sender, refund0); + } else { + TransferHelper.safeTransfer(params.token0, msg.sender, refund0); + } + } + if (amount1V3 < amount1V2) { + if (amount1V3 < amount1V2ToMigrate) { + TransferHelper.safeApprove(params.token1, nonfungiblePositionManager, 0); + } + + uint256 refund1 = amount1V2 - amount1V3; + if (params.refundAsETH && params.token1 == WETH9) { + IWETH9(WETH9).withdraw(refund1); + TransferHelper.safeTransferETH(msg.sender, refund1); + } else { + TransferHelper.safeTransfer(params.token1, msg.sender, refund1); + } + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/BlockTimestamp.sol b/lib/uniswap/v3-periphery/contracts/base/BlockTimestamp.sol new file mode 100644 index 0000000..ac05cac --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/BlockTimestamp.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +/// @title Function for getting block timestamp +/// @dev Base contract that is overridden for tests +abstract contract BlockTimestamp { + /// @dev Method that exists purely to be overridden for tests + /// @return The current block timestamp + function _blockTimestamp() internal view virtual returns (uint256) { + return block.timestamp; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/ERC721Permit.sol b/lib/uniswap/v3-periphery/contracts/base/ERC721Permit.sol new file mode 100644 index 0000000..efe0f54 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/ERC721Permit.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; +import '@openzeppelin/contracts/utils/Address.sol'; + +import '../libraries/ChainId.sol'; +import '../interfaces/external/IERC1271.sol'; +import '../interfaces/IERC721Permit.sol'; +import './BlockTimestamp.sol'; + +/// @title ERC721 with permit +/// @notice Nonfungible tokens that support an approve via signature, i.e. permit +abstract contract ERC721Permit is BlockTimestamp, ERC721, IERC721Permit { + /// @dev Gets the current nonce for a token ID and then increments it, returning the original value + function _getAndIncrementNonce(uint256 tokenId) internal virtual returns (uint256); + + /// @dev The hash of the name used in the permit signature verification + bytes32 private immutable nameHash; + + /// @dev The hash of the version string used in the permit signature verification + bytes32 private immutable versionHash; + + /// @notice Computes the nameHash and versionHash + constructor( + string memory name_, + string memory symbol_, + string memory version_ + ) ERC721(name_, symbol_) { + nameHash = keccak256(bytes(name_)); + versionHash = keccak256(bytes(version_)); + } + + /// @inheritdoc IERC721Permit + function DOMAIN_SEPARATOR() public view override returns (bytes32) { + return + keccak256( + abi.encode( + // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, + nameHash, + versionHash, + ChainId.get(), + address(this) + ) + ); + } + + /// @inheritdoc IERC721Permit + /// @dev Value is equal to keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"); + bytes32 public constant override PERMIT_TYPEHASH = + 0x49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad; + + /// @inheritdoc IERC721Permit + function permit( + address spender, + uint256 tokenId, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable override { + require(_blockTimestamp() <= deadline, 'Permit expired'); + + bytes32 digest = + keccak256( + abi.encodePacked( + '\x19\x01', + DOMAIN_SEPARATOR(), + keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, _getAndIncrementNonce(tokenId), deadline)) + ) + ); + address owner = ownerOf(tokenId); + require(spender != owner, 'ERC721Permit: approval to current owner'); + + if (Address.isContract(owner)) { + require(IERC1271(owner).isValidSignature(digest, abi.encodePacked(r, s, v)) == 0x1626ba7e, 'Unauthorized'); + } else { + address recoveredAddress = ecrecover(digest, v, r, s); + require(recoveredAddress != address(0), 'Invalid signature'); + require(recoveredAddress == owner, 'Unauthorized'); + } + + _approve(spender, tokenId); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/LiquidityManagement.sol b/lib/uniswap/v3-periphery/contracts/base/LiquidityManagement.sol new file mode 100644 index 0000000..0301b39 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/LiquidityManagement.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol'; +import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; + +import '../libraries/PoolAddress.sol'; +import '../libraries/CallbackValidation.sol'; +import '../libraries/LiquidityAmounts.sol'; + +import './PeripheryPayments.sol'; +import './PeripheryImmutableState.sol'; + +/// @title Liquidity management functions +/// @notice Internal functions for safely managing liquidity in Uniswap V3 +abstract contract LiquidityManagement is IUniswapV3MintCallback, PeripheryImmutableState, PeripheryPayments { + struct MintCallbackData { + PoolAddress.PoolKey poolKey; + address payer; + } + + /// @inheritdoc IUniswapV3MintCallback + function uniswapV3MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external override { + MintCallbackData memory decoded = abi.decode(data, (MintCallbackData)); + CallbackValidation.verifyCallback(factory, decoded.poolKey); + + if (amount0Owed > 0) pay(decoded.poolKey.token0, decoded.payer, msg.sender, amount0Owed); + if (amount1Owed > 0) pay(decoded.poolKey.token1, decoded.payer, msg.sender, amount1Owed); + } + + struct AddLiquidityParams { + address token0; + address token1; + uint24 fee; + address recipient; + int24 tickLower; + int24 tickUpper; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + } + + /// @notice Add liquidity to an initialized pool + function addLiquidity(AddLiquidityParams memory params) + internal + returns ( + uint128 liquidity, + uint256 amount0, + uint256 amount1, + IUniswapV3Pool pool + ) + { + PoolAddress.PoolKey memory poolKey = + PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee}); + + pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + + // compute the liquidity amount + { + (uint160 sqrtPriceX96, , , , , , ) = pool.slot0(); + uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper); + + liquidity = LiquidityAmounts.getLiquidityForAmounts( + sqrtPriceX96, + sqrtRatioAX96, + sqrtRatioBX96, + params.amount0Desired, + params.amount1Desired + ); + } + + (amount0, amount1) = pool.mint( + params.recipient, + params.tickLower, + params.tickUpper, + liquidity, + abi.encode(MintCallbackData({poolKey: poolKey, payer: msg.sender})) + ); + + require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check'); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/Multicall.sol b/lib/uniswap/v3-periphery/contracts/base/Multicall.sol new file mode 100644 index 0000000..5a6384d --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/Multicall.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../interfaces/IMulticall.sol'; + +/// @title Multicall +/// @notice Enables calling multiple methods in a single call to the contract +abstract contract Multicall is IMulticall { + /// @inheritdoc IMulticall + function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) { + results = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + (bool success, bytes memory result) = address(this).delegatecall(data[i]); + + if (!success) { + // Next 5 lines from https://ethereum.stackexchange.com/a/83577 + if (result.length < 68) revert(); + assembly { + result := add(result, 0x04) + } + revert(abi.decode(result, (string))); + } + + results[i] = result; + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/PeripheryImmutableState.sol b/lib/uniswap/v3-periphery/contracts/base/PeripheryImmutableState.sol new file mode 100644 index 0000000..75267a4 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/PeripheryImmutableState.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import '../interfaces/IPeripheryImmutableState.sol'; + +/// @title Immutable state +/// @notice Immutable state used by periphery contracts +abstract contract PeripheryImmutableState is IPeripheryImmutableState { + /// @inheritdoc IPeripheryImmutableState + address public immutable override factory; + /// @inheritdoc IPeripheryImmutableState + address public immutable override WETH9; + + constructor(address _factory, address _WETH9) { + factory = _factory; + WETH9 = _WETH9; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/PeripheryPayments.sol b/lib/uniswap/v3-periphery/contracts/base/PeripheryPayments.sol new file mode 100644 index 0000000..8651e3e --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/PeripheryPayments.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +import '../interfaces/IPeripheryPayments.sol'; +import '../interfaces/external/IWETH9.sol'; + +import '../libraries/TransferHelper.sol'; + +import './PeripheryImmutableState.sol'; + +abstract contract PeripheryPayments is IPeripheryPayments, PeripheryImmutableState { + receive() external payable { + require(msg.sender == WETH9, 'Not WETH9'); + } + + /// @inheritdoc IPeripheryPayments + function unwrapWETH9(uint256 amountMinimum, address recipient) public payable override { + uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this)); + require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9'); + + if (balanceWETH9 > 0) { + IWETH9(WETH9).withdraw(balanceWETH9); + TransferHelper.safeTransferETH(recipient, balanceWETH9); + } + } + + /// @inheritdoc IPeripheryPayments + function sweepToken( + address token, + uint256 amountMinimum, + address recipient + ) public payable override { + uint256 balanceToken = IERC20(token).balanceOf(address(this)); + require(balanceToken >= amountMinimum, 'Insufficient token'); + + if (balanceToken > 0) { + TransferHelper.safeTransfer(token, recipient, balanceToken); + } + } + + /// @inheritdoc IPeripheryPayments + function refundETH() external payable override { + if (address(this).balance > 0) TransferHelper.safeTransferETH(msg.sender, address(this).balance); + } + + /// @param token The token to pay + /// @param payer The entity that must pay + /// @param recipient The entity that will receive payment + /// @param value The amount to pay + function pay( + address token, + address payer, + address recipient, + uint256 value + ) internal { + if (token == WETH9 && address(this).balance >= value) { + // pay with WETH9 + IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay + IWETH9(WETH9).transfer(recipient, value); + } else if (payer == address(this)) { + // pay with tokens already in the contract (for the exact input multihop case) + TransferHelper.safeTransfer(token, recipient, value); + } else { + // pull payment + TransferHelper.safeTransferFrom(token, payer, recipient, value); + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/PeripheryPaymentsWithFee.sol b/lib/uniswap/v3-periphery/contracts/base/PeripheryPaymentsWithFee.sol new file mode 100644 index 0000000..b7b1a31 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/PeripheryPaymentsWithFee.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol'; + +import './PeripheryPayments.sol'; +import '../interfaces/IPeripheryPaymentsWithFee.sol'; + +import '../interfaces/external/IWETH9.sol'; +import '../libraries/TransferHelper.sol'; + +abstract contract PeripheryPaymentsWithFee is PeripheryPayments, IPeripheryPaymentsWithFee { + using LowGasSafeMath for uint256; + + /// @inheritdoc IPeripheryPaymentsWithFee + function unwrapWETH9WithFee( + uint256 amountMinimum, + address recipient, + uint256 feeBips, + address feeRecipient + ) public payable override { + require(feeBips > 0 && feeBips <= 100); + + uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this)); + require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9'); + + if (balanceWETH9 > 0) { + IWETH9(WETH9).withdraw(balanceWETH9); + uint256 feeAmount = balanceWETH9.mul(feeBips) / 10_000; + if (feeAmount > 0) TransferHelper.safeTransferETH(feeRecipient, feeAmount); + TransferHelper.safeTransferETH(recipient, balanceWETH9 - feeAmount); + } + } + + /// @inheritdoc IPeripheryPaymentsWithFee + function sweepTokenWithFee( + address token, + uint256 amountMinimum, + address recipient, + uint256 feeBips, + address feeRecipient + ) public payable override { + require(feeBips > 0 && feeBips <= 100); + + uint256 balanceToken = IERC20(token).balanceOf(address(this)); + require(balanceToken >= amountMinimum, 'Insufficient token'); + + if (balanceToken > 0) { + uint256 feeAmount = balanceToken.mul(feeBips) / 10_000; + if (feeAmount > 0) TransferHelper.safeTransfer(token, feeRecipient, feeAmount); + TransferHelper.safeTransfer(token, recipient, balanceToken - feeAmount); + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/PeripheryValidation.sol b/lib/uniswap/v3-periphery/contracts/base/PeripheryValidation.sol new file mode 100644 index 0000000..40fadd7 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/PeripheryValidation.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import './BlockTimestamp.sol'; + +abstract contract PeripheryValidation is BlockTimestamp { + modifier checkDeadline(uint256 deadline) { + require(_blockTimestamp() <= deadline, 'Transaction too old'); + _; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/PoolInitializer.sol b/lib/uniswap/v3-periphery/contracts/base/PoolInitializer.sol new file mode 100644 index 0000000..af8e43e --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/PoolInitializer.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +import './PeripheryImmutableState.sol'; +import '../interfaces/IPoolInitializer.sol'; + +/// @title Creates and initializes V3 Pools +abstract contract PoolInitializer is IPoolInitializer, PeripheryImmutableState { + /// @inheritdoc IPoolInitializer + function createAndInitializePoolIfNecessary( + address token0, + address token1, + uint24 fee, + uint160 sqrtPriceX96 + ) external payable override returns (address pool) { + require(token0 < token1); + pool = IUniswapV3Factory(factory).getPool(token0, token1, fee); + + if (pool == address(0)) { + pool = IUniswapV3Factory(factory).createPool(token0, token1, fee); + IUniswapV3Pool(pool).initialize(sqrtPriceX96); + } else { + (uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0(); + if (sqrtPriceX96Existing == 0) { + IUniswapV3Pool(pool).initialize(sqrtPriceX96); + } + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/base/SelfPermit.sol b/lib/uniswap/v3-periphery/contracts/base/SelfPermit.sol new file mode 100644 index 0000000..26501e8 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/base/SelfPermit.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol'; + +import '../interfaces/ISelfPermit.sol'; +import '../interfaces/external/IERC20PermitAllowed.sol'; + +/// @title Self Permit +/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route +/// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a contract and call a function +/// that requires an approval in a single transaction. +abstract contract SelfPermit is ISelfPermit { + /// @inheritdoc ISelfPermit + function selfPermit( + address token, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public payable override { + IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s); + } + + /// @inheritdoc ISelfPermit + function selfPermitIfNecessary( + address token, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable override { + if (IERC20(token).allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s); + } + + /// @inheritdoc ISelfPermit + function selfPermitAllowed( + address token, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) public payable override { + IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s); + } + + /// @inheritdoc ISelfPermit + function selfPermitAllowedIfNecessary( + address token, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external payable override { + if (IERC20(token).allowance(msg.sender, address(this)) < type(uint256).max) + selfPermitAllowed(token, nonce, expiry, v, r, s); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/examples/PairFlash.sol b/lib/uniswap/v3-periphery/contracts/examples/PairFlash.sol new file mode 100644 index 0000000..a538b41 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/examples/PairFlash.sol @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol'; +import '@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol'; + +import '../base/PeripheryPayments.sol'; +import '../base/PeripheryImmutableState.sol'; +import '../libraries/PoolAddress.sol'; +import '../libraries/CallbackValidation.sol'; +import '../libraries/TransferHelper.sol'; +import '../interfaces/ISwapRouter.sol'; + +/// @title Flash contract implementation +/// @notice An example contract using the Uniswap V3 flash function +contract PairFlash is IUniswapV3FlashCallback, PeripheryPayments { + using LowGasSafeMath for uint256; + using LowGasSafeMath for int256; + + ISwapRouter public immutable swapRouter; + + constructor( + ISwapRouter _swapRouter, + address _factory, + address _WETH9 + ) PeripheryImmutableState(_factory, _WETH9) { + swapRouter = _swapRouter; + } + + // fee2 and fee3 are the two other fees associated with the two other pools of token0 and token1 + struct FlashCallbackData { + uint256 amount0; + uint256 amount1; + address payer; + PoolAddress.PoolKey poolKey; + uint24 poolFee2; + uint24 poolFee3; + } + + /// @param fee0 The fee from calling flash for token0 + /// @param fee1 The fee from calling flash for token1 + /// @param data The data needed in the callback passed as FlashCallbackData from `initFlash` + /// @notice implements the callback called from flash + /// @dev fails if the flash is not profitable, meaning the amountOut from the flash is less than the amount borrowed + function uniswapV3FlashCallback( + uint256 fee0, + uint256 fee1, + bytes calldata data + ) external override { + FlashCallbackData memory decoded = abi.decode(data, (FlashCallbackData)); + CallbackValidation.verifyCallback(factory, decoded.poolKey); + + address token0 = decoded.poolKey.token0; + address token1 = decoded.poolKey.token1; + + // profitability parameters - we must receive at least the required payment from the arbitrage swaps + // exactInputSingle will fail if this amount not met + uint256 amount0Min = LowGasSafeMath.add(decoded.amount0, fee0); + uint256 amount1Min = LowGasSafeMath.add(decoded.amount1, fee1); + + // call exactInputSingle for swapping token1 for token0 in pool with fee2 + TransferHelper.safeApprove(token1, address(swapRouter), decoded.amount1); + uint256 amountOut0 = + swapRouter.exactInputSingle( + ISwapRouter.ExactInputSingleParams({ + tokenIn: token1, + tokenOut: token0, + fee: decoded.poolFee2, + recipient: address(this), + deadline: block.timestamp, + amountIn: decoded.amount1, + amountOutMinimum: amount0Min, + sqrtPriceLimitX96: 0 + }) + ); + + // call exactInputSingle for swapping token0 for token 1 in pool with fee3 + TransferHelper.safeApprove(token0, address(swapRouter), decoded.amount0); + uint256 amountOut1 = + swapRouter.exactInputSingle( + ISwapRouter.ExactInputSingleParams({ + tokenIn: token0, + tokenOut: token1, + fee: decoded.poolFee3, + recipient: address(this), + deadline: block.timestamp, + amountIn: decoded.amount0, + amountOutMinimum: amount1Min, + sqrtPriceLimitX96: 0 + }) + ); + + // pay the required amounts back to the pair + if (amount0Min > 0) pay(token0, address(this), msg.sender, amount0Min); + if (amount1Min > 0) pay(token1, address(this), msg.sender, amount1Min); + + // if profitable pay profits to payer + if (amountOut0 > amount0Min) { + uint256 profit0 = amountOut0 - amount0Min; + pay(token0, address(this), decoded.payer, profit0); + } + if (amountOut1 > amount1Min) { + uint256 profit1 = amountOut1 - amount1Min; + pay(token1, address(this), decoded.payer, profit1); + } + } + + //fee1 is the fee of the pool from the initial borrow + //fee2 is the fee of the first pool to arb from + //fee3 is the fee of the second pool to arb from + struct FlashParams { + address token0; + address token1; + uint24 fee1; + uint256 amount0; + uint256 amount1; + uint24 fee2; + uint24 fee3; + } + + /// @param params The parameters necessary for flash and the callback, passed in as FlashParams + /// @notice Calls the pools flash function with data needed in `uniswapV3FlashCallback` + function initFlash(FlashParams memory params) external { + PoolAddress.PoolKey memory poolKey = + PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee1}); + IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + // recipient of borrowed amounts + // amount of token0 requested to borrow + // amount of token1 requested to borrow + // need amount 0 and amount1 in callback to pay back pool + // recipient of flash should be THIS contract + pool.flash( + address(this), + params.amount0, + params.amount1, + abi.encode( + FlashCallbackData({ + amount0: params.amount0, + amount1: params.amount1, + payer: msg.sender, + poolKey: poolKey, + poolFee2: params.fee2, + poolFee3: params.fee3 + }) + ) + ); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IERC20Metadata.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IERC20Metadata.sol new file mode 100644 index 0000000..d7bd3e9 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IERC20Metadata.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.7.0; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +/// @title IERC20Metadata +/// @title Interface for ERC20 Metadata +/// @notice Extension to IERC20 that includes token metadata +interface IERC20Metadata is IERC20 { + /// @return The name of the token + function name() external view returns (string memory); + + /// @return The symbol of the token + function symbol() external view returns (string memory); + + /// @return The number of decimal places the token has + function decimals() external view returns (uint8); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol new file mode 100644 index 0000000..744cd3a --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; + +import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; + +/// @title ERC721 with permit +/// @notice Extension to ERC721 that includes a permit function for signature based approvals +interface IERC721Permit is IERC721 { + /// @notice The permit typehash used in the permit signature + /// @return The typehash for the permit + function PERMIT_TYPEHASH() external pure returns (bytes32); + + /// @notice The domain separator used in the permit signature + /// @return The domain seperator used in encoding of permit signature + function DOMAIN_SEPARATOR() external view returns (bytes32); + + /// @notice Approve of a specific token ID for spending by spender via signature + /// @param spender The account that is being approved + /// @param tokenId The ID of the token that is being approved for spending + /// @param deadline The deadline timestamp by which the call must be mined for the approve to work + /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` + /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` + /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` + function permit( + address spender, + uint256 tokenId, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IMulticall.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IMulticall.sol new file mode 100644 index 0000000..1ea39c6 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IMulticall.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +/// @title Multicall interface +/// @notice Enables calling multiple methods in a single call to the contract +interface IMulticall { + /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed + /// @dev The `msg.value` should not be trusted for any method callable from multicall. + /// @param data The encoded function data for each of the calls to make to this contract + /// @return results The results from each of the calls passed in via data + function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol b/lib/uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol new file mode 100644 index 0000000..023b266 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +import '@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol'; +import '@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol'; + +import './IPoolInitializer.sol'; +import './IERC721Permit.sol'; +import './IPeripheryPayments.sol'; +import './IPeripheryImmutableState.sol'; +import '../libraries/PoolAddress.sol'; + +/// @title Non-fungible token for positions +/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred +/// and authorized. +interface INonfungiblePositionManager is + IPoolInitializer, + IPeripheryPayments, + IPeripheryImmutableState, + IERC721Metadata, + IERC721Enumerable, + IERC721Permit +{ + /// @notice Emitted when liquidity is increased for a position NFT + /// @dev Also emitted when a token is minted + /// @param tokenId The ID of the token for which liquidity was increased + /// @param liquidity The amount by which liquidity for the NFT position was increased + /// @param amount0 The amount of token0 that was paid for the increase in liquidity + /// @param amount1 The amount of token1 that was paid for the increase in liquidity + event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); + /// @notice Emitted when liquidity is decreased for a position NFT + /// @param tokenId The ID of the token for which liquidity was decreased + /// @param liquidity The amount by which liquidity for the NFT position was decreased + /// @param amount0 The amount of token0 that was accounted for the decrease in liquidity + /// @param amount1 The amount of token1 that was accounted for the decrease in liquidity + event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); + /// @notice Emitted when tokens are collected for a position NFT + /// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior + /// @param tokenId The ID of the token for which underlying tokens were collected + /// @param recipient The address of the account that received the collected tokens + /// @param amount0 The amount of token0 owed to the position that was collected + /// @param amount1 The amount of token1 owed to the position that was collected + event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1); + + /// @notice Returns the position information associated with a given token ID. + /// @dev Throws if the token ID is not valid. + /// @param tokenId The ID of the token that represents the position + /// @return nonce The nonce for permits + /// @return operator The address that is approved for spending + /// @return token0 The address of the token0 for a specific pool + /// @return token1 The address of the token1 for a specific pool + /// @return fee The fee associated with the pool + /// @return tickLower The lower end of the tick range for the position + /// @return tickUpper The higher end of the tick range for the position + /// @return liquidity The liquidity of the position + /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position + /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position + /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation + /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation + function positions(uint256 tokenId) + external + view + returns ( + uint96 nonce, + address operator, + address token0, + address token1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + + struct MintParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + address recipient; + uint256 deadline; + } + + /// @notice Creates a new position wrapped in a NFT + /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized + /// a method does not exist, i.e. the pool is assumed to be initialized. + /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata + /// @return tokenId The ID of the token that represents the minted position + /// @return liquidity The amount of liquidity for this position + /// @return amount0 The amount of token0 + /// @return amount1 The amount of token1 + function mint(MintParams calldata params) + external + payable + returns ( + uint256 tokenId, + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ); + + struct IncreaseLiquidityParams { + uint256 tokenId; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } + + /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender` + /// @param params tokenId The ID of the token for which liquidity is being increased, + /// amount0Desired The desired amount of token0 to be spent, + /// amount1Desired The desired amount of token1 to be spent, + /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check, + /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check, + /// deadline The time by which the transaction must be included to effect the change + /// @return liquidity The new liquidity amount as a result of the increase + /// @return amount0 The amount of token0 to acheive resulting liquidity + /// @return amount1 The amount of token1 to acheive resulting liquidity + function increaseLiquidity(IncreaseLiquidityParams calldata params) + external + payable + returns ( + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ); + + struct DecreaseLiquidityParams { + uint256 tokenId; + uint128 liquidity; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } + + /// @notice Decreases the amount of liquidity in a position and accounts it to the position + /// @param params tokenId The ID of the token for which liquidity is being decreased, + /// amount The amount by which liquidity will be decreased, + /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity, + /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity, + /// deadline The time by which the transaction must be included to effect the change + /// @return amount0 The amount of token0 accounted to the position's tokens owed + /// @return amount1 The amount of token1 accounted to the position's tokens owed + function decreaseLiquidity(DecreaseLiquidityParams calldata params) + external + payable + returns (uint256 amount0, uint256 amount1); + + struct CollectParams { + uint256 tokenId; + address recipient; + uint128 amount0Max; + uint128 amount1Max; + } + + /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient + /// @param params tokenId The ID of the NFT for which tokens are being collected, + /// recipient The account that should receive the tokens, + /// amount0Max The maximum amount of token0 to collect, + /// amount1Max The maximum amount of token1 to collect + /// @return amount0 The amount of fees collected in token0 + /// @return amount1 The amount of fees collected in token1 + function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); + + /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens + /// must be collected first. + /// @param tokenId The ID of the token that is being burned + function burn(uint256 tokenId) external payable; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/INonfungibleTokenPositionDescriptor.sol b/lib/uniswap/v3-periphery/contracts/interfaces/INonfungibleTokenPositionDescriptor.sol new file mode 100644 index 0000000..b3f27b8 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/INonfungibleTokenPositionDescriptor.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import './INonfungiblePositionManager.sol'; + +/// @title Describes position NFT tokens via URI +interface INonfungibleTokenPositionDescriptor { + /// @notice Produces the URI describing a particular token ID for a position manager + /// @dev Note this URI may be a data: URI with the JSON contents directly inlined + /// @param positionManager The position manager for which to describe the token + /// @param tokenId The ID of the token for which to produce a description, which may not be valid + /// @return The URI of the ERC721-compliant metadata + function tokenURI(INonfungiblePositionManager positionManager, uint256 tokenId) + external + view + returns (string memory); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol new file mode 100644 index 0000000..b337805 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Immutable state +/// @notice Functions that return immutable state of the router +interface IPeripheryImmutableState { + /// @return Returns the address of the Uniswap V3 factory + function factory() external view returns (address); + + /// @return Returns the address of WETH9 + function WETH9() external view returns (address); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol new file mode 100644 index 0000000..ac74f8c --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; + +/// @title Periphery Payments +/// @notice Functions to ease deposits and withdrawals of ETH +interface IPeripheryPayments { + /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH. + /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users. + /// @param amountMinimum The minimum amount of WETH9 to unwrap + /// @param recipient The address receiving ETH + function unwrapWETH9(uint256 amountMinimum, address recipient) external payable; + + /// @notice Refunds any ETH balance held by this contract to the `msg.sender` + /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps + /// that use ether for the input amount + function refundETH() external payable; + + /// @notice Transfers the full amount of a token held by this contract to recipient + /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users + /// @param token The contract address of the token which will be transferred to `recipient` + /// @param amountMinimum The minimum amount of token required for a transfer + /// @param recipient The destination address of the token + function sweepToken( + address token, + uint256 amountMinimum, + address recipient + ) external payable; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryPaymentsWithFee.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryPaymentsWithFee.sol new file mode 100644 index 0000000..907e446 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IPeripheryPaymentsWithFee.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; + +import './IPeripheryPayments.sol'; + +/// @title Periphery Payments +/// @notice Functions to ease deposits and withdrawals of ETH +interface IPeripheryPaymentsWithFee is IPeripheryPayments { + /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH, with a percentage between + /// 0 (exclusive), and 1 (inclusive) going to feeRecipient + /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users. + function unwrapWETH9WithFee( + uint256 amountMinimum, + address recipient, + uint256 feeBips, + address feeRecipient + ) external payable; + + /// @notice Transfers the full amount of a token held by this contract to recipient, with a percentage between + /// 0 (exclusive) and 1 (inclusive) going to feeRecipient + /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users + function sweepTokenWithFee( + address token, + uint256 amountMinimum, + address recipient, + uint256 feeBips, + address feeRecipient + ) external payable; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IPoolInitializer.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IPoolInitializer.sol new file mode 100644 index 0000000..d2949b3 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IPoolInitializer.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +/// @title Creates and initializes V3 Pools +/// @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that +/// require the pool to exist. +interface IPoolInitializer { + /// @notice Creates a new pool if it does not exist, then initializes if not initialized + /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool + /// @param token0 The contract address of token0 of the pool + /// @param token1 The contract address of token1 of the pool + /// @param fee The fee amount of the v3 pool for the specified token pair + /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value + /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary + function createAndInitializePoolIfNecessary( + address token0, + address token1, + uint24 fee, + uint160 sqrtPriceX96 + ) external payable returns (address pool); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IQuoter.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IQuoter.sol new file mode 100644 index 0000000..3410e0c --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IQuoter.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +/// @title Quoter Interface +/// @notice Supports quoting the calculated amounts from exact input or exact output swaps +/// @dev These functions are not marked view because they rely on calling non-view functions and reverting +/// to compute the result. They are also not gas efficient and should not be called on-chain. +interface IQuoter { + /// @notice Returns the amount out received for a given exact input swap without executing the swap + /// @param path The path of the swap, i.e. each token pair and the pool fee + /// @param amountIn The amount of the first token to swap + /// @return amountOut The amount of the last token that would be received + function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut); + + /// @notice Returns the amount out received for a given exact input but for a swap of a single pool + /// @param tokenIn The token being swapped in + /// @param tokenOut The token being swapped out + /// @param fee The fee of the token pool to consider for the pair + /// @param amountIn The desired input amount + /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap + /// @return amountOut The amount of `tokenOut` that would be received + function quoteExactInputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountIn, + uint160 sqrtPriceLimitX96 + ) external returns (uint256 amountOut); + + /// @notice Returns the amount in required for a given exact output swap without executing the swap + /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order + /// @param amountOut The amount of the last token to receive + /// @return amountIn The amount of first token required to be paid + function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn); + + /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool + /// @param tokenIn The token being swapped in + /// @param tokenOut The token being swapped out + /// @param fee The fee of the token pool to consider for the pair + /// @param amountOut The desired output amount + /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap + /// @return amountIn The amount required as the input for the swap in order to receive `amountOut` + function quoteExactOutputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountOut, + uint160 sqrtPriceLimitX96 + ) external returns (uint256 amountIn); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol new file mode 100644 index 0000000..3c2961b --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +/// @title QuoterV2 Interface +/// @notice Supports quoting the calculated amounts from exact input or exact output swaps. +/// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the pool after the swap. +/// @dev These functions are not marked view because they rely on calling non-view functions and reverting +/// to compute the result. They are also not gas efficient and should not be called on-chain. +interface IQuoterV2 { + /// @notice Returns the amount out received for a given exact input swap without executing the swap + /// @param path The path of the swap, i.e. each token pair and the pool fee + /// @param amountIn The amount of the first token to swap + /// @return amountOut The amount of the last token that would be received + /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path + /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path + /// @return gasEstimate The estimate of the gas that the swap consumes + function quoteExactInput(bytes memory path, uint256 amountIn) + external + returns ( + uint256 amountOut, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ); + + struct QuoteExactInputSingleParams { + address tokenIn; + address tokenOut; + uint256 amountIn; + uint24 fee; + uint160 sqrtPriceLimitX96; + } + + /// @notice Returns the amount out received for a given exact input but for a swap of a single pool + /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams` + /// tokenIn The token being swapped in + /// tokenOut The token being swapped out + /// fee The fee of the token pool to consider for the pair + /// amountIn The desired input amount + /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap + /// @return amountOut The amount of `tokenOut` that would be received + /// @return sqrtPriceX96After The sqrt price of the pool after the swap + /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed + /// @return gasEstimate The estimate of the gas that the swap consumes + function quoteExactInputSingle(QuoteExactInputSingleParams memory params) + external + returns ( + uint256 amountOut, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ); + + /// @notice Returns the amount in required for a given exact output swap without executing the swap + /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order + /// @param amountOut The amount of the last token to receive + /// @return amountIn The amount of first token required to be paid + /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path + /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path + /// @return gasEstimate The estimate of the gas that the swap consumes + function quoteExactOutput(bytes memory path, uint256 amountOut) + external + returns ( + uint256 amountIn, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ); + + struct QuoteExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint256 amount; + uint24 fee; + uint160 sqrtPriceLimitX96; + } + + /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool + /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams` + /// tokenIn The token being swapped in + /// tokenOut The token being swapped out + /// fee The fee of the token pool to consider for the pair + /// amountOut The desired output amount + /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap + /// @return amountIn The amount required as the input for the swap in order to receive `amountOut` + /// @return sqrtPriceX96After The sqrt price of the pool after the swap + /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed + /// @return gasEstimate The estimate of the gas that the swap consumes + function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) + external + returns ( + uint256 amountIn, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/ISelfPermit.sol b/lib/uniswap/v3-periphery/contracts/interfaces/ISelfPermit.sol new file mode 100644 index 0000000..0ab8a25 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/ISelfPermit.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; + +/// @title Self Permit +/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route +interface ISelfPermit { + /// @notice Permits this contract to spend a given token from `msg.sender` + /// @dev The `owner` is always msg.sender and the `spender` is always address(this). + /// @param token The address of the token spent + /// @param value The amount that can be spent of token + /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp + /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` + /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` + /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` + function selfPermit( + address token, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + + /// @notice Permits this contract to spend a given token from `msg.sender` + /// @dev The `owner` is always msg.sender and the `spender` is always address(this). + /// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit + /// @param token The address of the token spent + /// @param value The amount that can be spent of token + /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp + /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` + /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` + /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` + function selfPermitIfNecessary( + address token, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + + /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter + /// @dev The `owner` is always msg.sender and the `spender` is always address(this) + /// @param token The address of the token spent + /// @param nonce The current nonce of the owner + /// @param expiry The timestamp at which the permit is no longer valid + /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` + /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` + /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` + function selfPermitAllowed( + address token, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + + /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter + /// @dev The `owner` is always msg.sender and the `spender` is always address(this) + /// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed. + /// @param token The address of the token spent + /// @param nonce The current nonce of the owner + /// @param expiry The timestamp at which the permit is no longer valid + /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` + /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` + /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` + function selfPermitAllowedIfNecessary( + address token, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol b/lib/uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol new file mode 100644 index 0000000..35806a2 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol'; + +/// @title Router token swapping functionality +/// @notice Functions for swapping tokens via Uniswap V3 +interface ISwapRouter is IUniswapV3SwapCallback { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another token + /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata + /// @return amountOut The amount of the received token + function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); + + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata + /// @return amountOut The amount of the received token + function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut); + + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another token + /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata + /// @return amountIn The amount of the input token + function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata + /// @return amountIn The amount of the input token + function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/ITickLens.sol b/lib/uniswap/v3-periphery/contracts/interfaces/ITickLens.sol new file mode 100644 index 0000000..89bac2d --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/ITickLens.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +/// @title Tick Lens +/// @notice Provides functions for fetching chunks of tick data for a pool +/// @dev This avoids the waterfall of fetching the tick bitmap, parsing the bitmap to know which ticks to fetch, and +/// then sending additional multicalls to fetch the tick data +interface ITickLens { + struct PopulatedTick { + int24 tick; + int128 liquidityNet; + uint128 liquidityGross; + } + + /// @notice Get all the tick data for the populated ticks from a word of the tick bitmap of a pool + /// @param pool The address of the pool for which to fetch populated tick data + /// @param tickBitmapIndex The index of the word in the tick bitmap for which to parse the bitmap and + /// fetch all the populated ticks + /// @return populatedTicks An array of tick data for the given word in the tick bitmap + function getPopulatedTicksInWord(address pool, int16 tickBitmapIndex) + external + view + returns (PopulatedTick[] memory populatedTicks); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/IV3Migrator.sol b/lib/uniswap/v3-periphery/contracts/interfaces/IV3Migrator.sol new file mode 100644 index 0000000..6eb6e42 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/IV3Migrator.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +import './IMulticall.sol'; +import './ISelfPermit.sol'; +import './IPoolInitializer.sol'; + +/// @title V3 Migrator +/// @notice Enables migration of liqudity from Uniswap v2-compatible pairs into Uniswap v3 pools +interface IV3Migrator is IMulticall, ISelfPermit, IPoolInitializer { + struct MigrateParams { + address pair; // the Uniswap v2-compatible pair + uint256 liquidityToMigrate; // expected to be balanceOf(msg.sender) + uint8 percentageToMigrate; // represented as a numerator over 100 + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint256 amount0Min; // must be discounted by percentageToMigrate + uint256 amount1Min; // must be discounted by percentageToMigrate + address recipient; + uint256 deadline; + bool refundAsETH; + } + + /// @notice Migrates liquidity to v3 by burning v2 liquidity and minting a new position for v3 + /// @dev Slippage protection is enforced via `amount{0,1}Min`, which should be a discount of the expected values of + /// the maximum amount of v3 liquidity that the v2 liquidity can get. For the special case of migrating to an + /// out-of-range position, `amount{0,1}Min` may be set to 0, enforcing that the position remains out of range + /// @param params The params necessary to migrate v2 liquidity, encoded as `MigrateParams` in calldata + function migrate(MigrateParams calldata params) external; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/external/IERC1271.sol b/lib/uniswap/v3-periphery/contracts/interfaces/external/IERC1271.sol new file mode 100644 index 0000000..824b03e --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/external/IERC1271.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Interface for verifying contract-based account signatures +/// @notice Interface that verifies provided signature for the data +/// @dev Interface defined by EIP-1271 +interface IERC1271 { + /// @notice Returns whether the provided signature is valid for the provided data + /// @dev MUST return the bytes4 magic value 0x1626ba7e when function passes. + /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5). + /// MUST allow external calls. + /// @param hash Hash of the data to be signed + /// @param signature Signature byte array associated with _data + /// @return magicValue The bytes4 magic value 0x1626ba7e + function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/external/IERC20PermitAllowed.sol b/lib/uniswap/v3-periphery/contracts/interfaces/external/IERC20PermitAllowed.sol new file mode 100644 index 0000000..a817e1c --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/external/IERC20PermitAllowed.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Interface for permit +/// @notice Interface used by DAI/CHAI for permit +interface IERC20PermitAllowed { + /// @notice Approve the spender to spend some tokens via the holder signature + /// @dev This is the permit interface used by DAI and CHAI + /// @param holder The address of the token holder, the token owner + /// @param spender The address of the token spender + /// @param nonce The holder's nonce, increases at each call to permit + /// @param expiry The timestamp at which the permit is no longer valid + /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0 + /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s` + /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` + /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` + function permit( + address holder, + address spender, + uint256 nonce, + uint256 expiry, + bool allowed, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} diff --git a/lib/uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol b/lib/uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol new file mode 100644 index 0000000..58ab43e --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/interfaces/external/IWETH9.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +/// @title Interface for WETH9 +interface IWETH9 is IERC20 { + /// @notice Deposit ether to get wrapped ether + function deposit() external payable; + + /// @notice Withdraw wrapped ether to get ether + function withdraw(uint256) external; +} diff --git a/lib/uniswap/v3-periphery/contracts/lens/Quoter.sol b/lib/uniswap/v3-periphery/contracts/lens/Quoter.sol new file mode 100644 index 0000000..f4a9ed6 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/lens/Quoter.sol @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/libraries/SafeCast.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol'; + +import '../interfaces/IQuoter.sol'; +import '../base/PeripheryImmutableState.sol'; +import '../libraries/Path.sol'; +import '../libraries/PoolAddress.sol'; +import '../libraries/CallbackValidation.sol'; + +/// @title Provides quotes for swaps +/// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap +/// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute +/// the swap and check the amounts in the callback. +contract Quoter is IQuoter, IUniswapV3SwapCallback, PeripheryImmutableState { + using Path for bytes; + using SafeCast for uint256; + + /// @dev Transient storage variable used to check a safety condition in exact output swaps. + uint256 private amountOutCached; + + constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} + + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) private view returns (IUniswapV3Pool) { + return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee))); + } + + /// @inheritdoc IUniswapV3SwapCallback + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes memory path + ) external view override { + require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); + + (bool isExactInput, uint256 amountToPay, uint256 amountReceived) = + amount0Delta > 0 + ? (tokenIn < tokenOut, uint256(amount0Delta), uint256(-amount1Delta)) + : (tokenOut < tokenIn, uint256(amount1Delta), uint256(-amount0Delta)); + if (isExactInput) { + assembly { + let ptr := mload(0x40) + mstore(ptr, amountReceived) + revert(ptr, 32) + } + } else { + // if the cache has been populated, ensure that the full output amount has been received + if (amountOutCached != 0) require(amountReceived == amountOutCached); + assembly { + let ptr := mload(0x40) + mstore(ptr, amountToPay) + revert(ptr, 32) + } + } + } + + /// @dev Parses a revert reason that should contain the numeric quote + function parseRevertReason(bytes memory reason) private pure returns (uint256) { + if (reason.length != 32) { + if (reason.length < 68) revert('Unexpected error'); + assembly { + reason := add(reason, 0x04) + } + revert(abi.decode(reason, (string))); + } + return abi.decode(reason, (uint256)); + } + + /// @inheritdoc IQuoter + function quoteExactInputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountIn, + uint160 sqrtPriceLimitX96 + ) public override returns (uint256 amountOut) { + bool zeroForOne = tokenIn < tokenOut; + + try + getPool(tokenIn, tokenOut, fee).swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + amountIn.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encodePacked(tokenIn, fee, tokenOut) + ) + {} catch (bytes memory reason) { + return parseRevertReason(reason); + } + } + + /// @inheritdoc IQuoter + function quoteExactInput(bytes memory path, uint256 amountIn) external override returns (uint256 amountOut) { + while (true) { + bool hasMultiplePools = path.hasMultiplePools(); + + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + + // the outputs of prior swaps become the inputs to subsequent ones + amountIn = quoteExactInputSingle(tokenIn, tokenOut, fee, amountIn, 0); + + // decide whether to continue or terminate + if (hasMultiplePools) { + path = path.skipToken(); + } else { + return amountIn; + } + } + } + + /// @inheritdoc IQuoter + function quoteExactOutputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountOut, + uint160 sqrtPriceLimitX96 + ) public override returns (uint256 amountIn) { + bool zeroForOne = tokenIn < tokenOut; + + // if no price limit has been specified, cache the output amount for comparison in the swap callback + if (sqrtPriceLimitX96 == 0) amountOutCached = amountOut; + try + getPool(tokenIn, tokenOut, fee).swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + -amountOut.toInt256(), + sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : sqrtPriceLimitX96, + abi.encodePacked(tokenOut, fee, tokenIn) + ) + {} catch (bytes memory reason) { + if (sqrtPriceLimitX96 == 0) delete amountOutCached; // clear cache + return parseRevertReason(reason); + } + } + + /// @inheritdoc IQuoter + function quoteExactOutput(bytes memory path, uint256 amountOut) external override returns (uint256 amountIn) { + while (true) { + bool hasMultiplePools = path.hasMultiplePools(); + + (address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool(); + + // the inputs of prior swaps become the outputs of subsequent ones + amountOut = quoteExactOutputSingle(tokenIn, tokenOut, fee, amountOut, 0); + + // decide whether to continue or terminate + if (hasMultiplePools) { + path = path.skipToken(); + } else { + return amountOut; + } + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/lens/QuoterV2.sol b/lib/uniswap/v3-periphery/contracts/lens/QuoterV2.sol new file mode 100644 index 0000000..b82dddd --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/lens/QuoterV2.sol @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/libraries/SafeCast.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-core/contracts/libraries/TickBitmap.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol'; + +import '../interfaces/IQuoterV2.sol'; +import '../base/PeripheryImmutableState.sol'; +import '../libraries/Path.sol'; +import '../libraries/PoolAddress.sol'; +import '../libraries/CallbackValidation.sol'; +import '../libraries/PoolTicksCounter.sol'; + +/// @title Provides quotes for swaps +/// @notice Allows getting the expected amount out or amount in for a given swap without executing the swap +/// @dev These functions are not gas efficient and should _not_ be called on chain. Instead, optimistically execute +/// the swap and check the amounts in the callback. +contract QuoterV2 is IQuoterV2, IUniswapV3SwapCallback, PeripheryImmutableState { + using Path for bytes; + using SafeCast for uint256; + using PoolTicksCounter for IUniswapV3Pool; + + /// @dev Transient storage variable used to check a safety condition in exact output swaps. + uint256 private amountOutCached; + + constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} + + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) private view returns (IUniswapV3Pool) { + return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee))); + } + + /// @inheritdoc IUniswapV3SwapCallback + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes memory path + ) external view override { + require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee); + + (bool isExactInput, uint256 amountToPay, uint256 amountReceived) = + amount0Delta > 0 + ? (tokenIn < tokenOut, uint256(amount0Delta), uint256(-amount1Delta)) + : (tokenOut < tokenIn, uint256(amount1Delta), uint256(-amount0Delta)); + + IUniswapV3Pool pool = getPool(tokenIn, tokenOut, fee); + (uint160 sqrtPriceX96After, int24 tickAfter, , , , , ) = pool.slot0(); + + if (isExactInput) { + assembly { + let ptr := mload(0x40) + mstore(ptr, amountReceived) + mstore(add(ptr, 0x20), sqrtPriceX96After) + mstore(add(ptr, 0x40), tickAfter) + revert(ptr, 96) + } + } else { + // if the cache has been populated, ensure that the full output amount has been received + if (amountOutCached != 0) require(amountReceived == amountOutCached); + assembly { + let ptr := mload(0x40) + mstore(ptr, amountToPay) + mstore(add(ptr, 0x20), sqrtPriceX96After) + mstore(add(ptr, 0x40), tickAfter) + revert(ptr, 96) + } + } + } + + /// @dev Parses a revert reason that should contain the numeric quote + function parseRevertReason(bytes memory reason) + private + pure + returns ( + uint256 amount, + uint160 sqrtPriceX96After, + int24 tickAfter + ) + { + if (reason.length != 96) { + if (reason.length < 68) revert('Unexpected error'); + assembly { + reason := add(reason, 0x04) + } + revert(abi.decode(reason, (string))); + } + return abi.decode(reason, (uint256, uint160, int24)); + } + + function handleRevert( + bytes memory reason, + IUniswapV3Pool pool, + uint256 gasEstimate + ) + private + view + returns ( + uint256 amount, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 + ) + { + int24 tickBefore; + int24 tickAfter; + (, tickBefore, , , , , ) = pool.slot0(); + (amount, sqrtPriceX96After, tickAfter) = parseRevertReason(reason); + + initializedTicksCrossed = pool.countInitializedTicksCrossed(tickBefore, tickAfter); + + return (amount, sqrtPriceX96After, initializedTicksCrossed, gasEstimate); + } + + function quoteExactInputSingle(QuoteExactInputSingleParams memory params) + public + override + returns ( + uint256 amountOut, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ) + { + bool zeroForOne = params.tokenIn < params.tokenOut; + IUniswapV3Pool pool = getPool(params.tokenIn, params.tokenOut, params.fee); + + uint256 gasBefore = gasleft(); + try + pool.swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + params.amountIn.toInt256(), + params.sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : params.sqrtPriceLimitX96, + abi.encodePacked(params.tokenIn, params.fee, params.tokenOut) + ) + {} catch (bytes memory reason) { + gasEstimate = gasBefore - gasleft(); + return handleRevert(reason, pool, gasEstimate); + } + } + + function quoteExactInput(bytes memory path, uint256 amountIn) + public + override + returns ( + uint256 amountOut, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ) + { + sqrtPriceX96AfterList = new uint160[](path.numPools()); + initializedTicksCrossedList = new uint32[](path.numPools()); + + uint256 i = 0; + while (true) { + (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool(); + + // the outputs of prior swaps become the inputs to subsequent ones + (uint256 _amountOut, uint160 _sqrtPriceX96After, uint32 _initializedTicksCrossed, uint256 _gasEstimate) = + quoteExactInputSingle( + QuoteExactInputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + fee: fee, + amountIn: amountIn, + sqrtPriceLimitX96: 0 + }) + ); + + sqrtPriceX96AfterList[i] = _sqrtPriceX96After; + initializedTicksCrossedList[i] = _initializedTicksCrossed; + amountIn = _amountOut; + gasEstimate += _gasEstimate; + i++; + + // decide whether to continue or terminate + if (path.hasMultiplePools()) { + path = path.skipToken(); + } else { + return (amountIn, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate); + } + } + } + + function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) + public + override + returns ( + uint256 amountIn, + uint160 sqrtPriceX96After, + uint32 initializedTicksCrossed, + uint256 gasEstimate + ) + { + bool zeroForOne = params.tokenIn < params.tokenOut; + IUniswapV3Pool pool = getPool(params.tokenIn, params.tokenOut, params.fee); + + // if no price limit has been specified, cache the output amount for comparison in the swap callback + if (params.sqrtPriceLimitX96 == 0) amountOutCached = params.amount; + uint256 gasBefore = gasleft(); + try + pool.swap( + address(this), // address(0) might cause issues with some tokens + zeroForOne, + -params.amount.toInt256(), + params.sqrtPriceLimitX96 == 0 + ? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1) + : params.sqrtPriceLimitX96, + abi.encodePacked(params.tokenOut, params.fee, params.tokenIn) + ) + {} catch (bytes memory reason) { + gasEstimate = gasBefore - gasleft(); + if (params.sqrtPriceLimitX96 == 0) delete amountOutCached; // clear cache + return handleRevert(reason, pool, gasEstimate); + } + } + + function quoteExactOutput(bytes memory path, uint256 amountOut) + public + override + returns ( + uint256 amountIn, + uint160[] memory sqrtPriceX96AfterList, + uint32[] memory initializedTicksCrossedList, + uint256 gasEstimate + ) + { + sqrtPriceX96AfterList = new uint160[](path.numPools()); + initializedTicksCrossedList = new uint32[](path.numPools()); + + uint256 i = 0; + while (true) { + (address tokenOut, address tokenIn, uint24 fee) = path.decodeFirstPool(); + + // the inputs of prior swaps become the outputs of subsequent ones + (uint256 _amountIn, uint160 _sqrtPriceX96After, uint32 _initializedTicksCrossed, uint256 _gasEstimate) = + quoteExactOutputSingle( + QuoteExactOutputSingleParams({ + tokenIn: tokenIn, + tokenOut: tokenOut, + amount: amountOut, + fee: fee, + sqrtPriceLimitX96: 0 + }) + ); + + sqrtPriceX96AfterList[i] = _sqrtPriceX96After; + initializedTicksCrossedList[i] = _initializedTicksCrossed; + amountOut = _amountIn; + gasEstimate += _gasEstimate; + i++; + + // decide whether to continue or terminate + if (path.hasMultiplePools()) { + path = path.skipToken(); + } else { + return (amountOut, sqrtPriceX96AfterList, initializedTicksCrossedList, gasEstimate); + } + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/lens/README.md b/lib/uniswap/v3-periphery/contracts/lens/README.md new file mode 100644 index 0000000..8359711 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/lens/README.md @@ -0,0 +1,4 @@ +# lens + +These contracts are not designed to be called on-chain. They simplify +fetching on-chain data from off-chain. diff --git a/lib/uniswap/v3-periphery/contracts/lens/TickLens.sol b/lib/uniswap/v3-periphery/contracts/lens/TickLens.sol new file mode 100644 index 0000000..5e9be5f --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/lens/TickLens.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +import '../interfaces/ITickLens.sol'; + +/// @title Tick Lens contract +contract TickLens is ITickLens { + /// @inheritdoc ITickLens + function getPopulatedTicksInWord(address pool, int16 tickBitmapIndex) + public + view + override + returns (PopulatedTick[] memory populatedTicks) + { + // fetch bitmap + uint256 bitmap = IUniswapV3Pool(pool).tickBitmap(tickBitmapIndex); + + // calculate the number of populated ticks + uint256 numberOfPopulatedTicks; + for (uint256 i = 0; i < 256; i++) { + if (bitmap & (1 << i) > 0) numberOfPopulatedTicks++; + } + + // fetch populated tick data + int24 tickSpacing = IUniswapV3Pool(pool).tickSpacing(); + populatedTicks = new PopulatedTick[](numberOfPopulatedTicks); + for (uint256 i = 0; i < 256; i++) { + if (bitmap & (1 << i) > 0) { + int24 populatedTick = ((int24(tickBitmapIndex) << 8) + int24(i)) * tickSpacing; + (uint128 liquidityGross, int128 liquidityNet, , , , , , ) = IUniswapV3Pool(pool).ticks(populatedTick); + populatedTicks[--numberOfPopulatedTicks] = PopulatedTick({ + tick: populatedTick, + liquidityNet: liquidityNet, + liquidityGross: liquidityGross + }); + } + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/lens/UniswapInterfaceMulticall.sol b/lib/uniswap/v3-periphery/contracts/lens/UniswapInterfaceMulticall.sol new file mode 100644 index 0000000..54c62c9 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/lens/UniswapInterfaceMulticall.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6; +pragma abicoder v2; + +/// @notice A fork of Multicall2 specifically tailored for the Uniswap Interface +contract UniswapInterfaceMulticall { + struct Call { + address target; + uint256 gasLimit; + bytes callData; + } + + struct Result { + bool success; + uint256 gasUsed; + bytes returnData; + } + + function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { + timestamp = block.timestamp; + } + + function getEthBalance(address addr) public view returns (uint256 balance) { + balance = addr.balance; + } + + function multicall(Call[] memory calls) public returns (uint256 blockNumber, Result[] memory returnData) { + blockNumber = block.number; + returnData = new Result[](calls.length); + for (uint256 i = 0; i < calls.length; i++) { + (address target, uint256 gasLimit, bytes memory callData) = + (calls[i].target, calls[i].gasLimit, calls[i].callData); + uint256 gasLeftBefore = gasleft(); + (bool success, bytes memory ret) = target.call{gas: gasLimit}(callData); + uint256 gasUsed = gasLeftBefore - gasleft(); + returnData[i] = Result(success, gasUsed, ret); + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/BytesLib.sol b/lib/uniswap/v3-periphery/contracts/libraries/BytesLib.sol new file mode 100644 index 0000000..ca1ae84 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/BytesLib.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * @title Solidity Bytes Arrays Utils + * @author Gonçalo Sá + * + * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. + * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. + */ +pragma solidity >=0.5.0 <0.8.0; + +library BytesLib { + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { + require(_length + 31 >= _length, 'slice_overflow'); + require(_start + _length >= _start, 'slice_overflow'); + require(_bytes.length >= _start + _length, 'slice_outOfBounds'); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + + function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + require(_start + 20 >= _start, 'toAddress_overflow'); + require(_bytes.length >= _start + 20, 'toAddress_outOfBounds'); + address tempAddress; + + assembly { + tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + } + + return tempAddress; + } + + function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { + require(_start + 3 >= _start, 'toUint24_overflow'); + require(_bytes.length >= _start + 3, 'toUint24_outOfBounds'); + uint24 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x3), _start)) + } + + return tempUint; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/CallbackValidation.sol b/lib/uniswap/v3-periphery/contracts/libraries/CallbackValidation.sol new file mode 100644 index 0000000..788109f --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/CallbackValidation.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import './PoolAddress.sol'; + +/// @notice Provides validation for callbacks from Uniswap V3 Pools +library CallbackValidation { + /// @notice Returns the address of a valid Uniswap V3 Pool + /// @param factory The contract address of the Uniswap V3 factory + /// @param tokenA The contract address of either token0 or token1 + /// @param tokenB The contract address of the other token + /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip + /// @return pool The V3 pool contract address + function verifyCallback( + address factory, + address tokenA, + address tokenB, + uint24 fee + ) internal view returns (IUniswapV3Pool pool) { + return verifyCallback(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee)); + } + + /// @notice Returns the address of a valid Uniswap V3 Pool + /// @param factory The contract address of the Uniswap V3 factory + /// @param poolKey The identifying key of the V3 pool + /// @return pool The V3 pool contract address + function verifyCallback(address factory, PoolAddress.PoolKey memory poolKey) + internal + view + returns (IUniswapV3Pool pool) + { + pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey)); + require(msg.sender == address(pool)); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/ChainId.sol b/lib/uniswap/v3-periphery/contracts/libraries/ChainId.sol new file mode 100644 index 0000000..72cb8b7 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/ChainId.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.0; + +/// @title Function for getting the current chain ID +library ChainId { + /// @dev Gets the current chain ID + /// @return chainId The current chain ID + function get() internal pure returns (uint256 chainId) { + assembly { + chainId := chainid() + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/HexStrings.sol b/lib/uniswap/v3-periphery/contracts/libraries/HexStrings.sol new file mode 100644 index 0000000..8f82288 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/HexStrings.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6; + +library HexStrings { + bytes16 internal constant ALPHABET = '0123456789abcdef'; + + /// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + /// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55 + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = '0'; + buffer[1] = 'x'; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = ALPHABET[value & 0xf]; + value >>= 4; + } + require(value == 0, 'Strings: hex length insufficient'); + return string(buffer); + } + + function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length); + for (uint256 i = buffer.length; i > 0; i--) { + buffer[i - 1] = ALPHABET[value & 0xf]; + value >>= 4; + } + return string(buffer); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol b/lib/uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol new file mode 100644 index 0000000..1427022 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; +import '@uniswap/v3-core/contracts/libraries/FixedPoint96.sol'; + +/// @title Liquidity amount functions +/// @notice Provides functions for computing liquidity amounts from token amounts and prices +library LiquidityAmounts { + /// @notice Downcasts uint256 to uint128 + /// @param x The uint258 to be downcasted + /// @return y The passed value, downcasted to uint128 + function toUint128(uint256 x) private pure returns (uint128 y) { + require((y = uint128(x)) == x); + } + + /// @notice Computes the amount of liquidity received for a given amount of token0 and price range + /// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary + /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary + /// @param amount0 The amount0 being sent in + /// @return liquidity The amount of returned liquidity + function getLiquidityForAmount0( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount0 + ) internal pure returns (uint128 liquidity) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96); + return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96)); + } + + /// @notice Computes the amount of liquidity received for a given amount of token1 and price range + /// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)). + /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary + /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary + /// @param amount1 The amount1 being sent in + /// @return liquidity The amount of returned liquidity + function getLiquidityForAmount1( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount1 + ) internal pure returns (uint128 liquidity) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + return toUint128(FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96)); + } + + /// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current + /// pool prices and the prices at the tick boundaries + /// @param sqrtRatioX96 A sqrt price representing the current pool prices + /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary + /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary + /// @param amount0 The amount of token0 being sent in + /// @param amount1 The amount of token1 being sent in + /// @return liquidity The maximum amount of liquidity received + function getLiquidityForAmounts( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount0, + uint256 amount1 + ) internal pure returns (uint128 liquidity) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + if (sqrtRatioX96 <= sqrtRatioAX96) { + liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0); + } else if (sqrtRatioX96 < sqrtRatioBX96) { + uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0); + uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1); + + liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1; + } else { + liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1); + } + } + + /// @notice Computes the amount of token0 for a given amount of liquidity and a price range + /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary + /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary + /// @param liquidity The liquidity being valued + /// @return amount0 The amount of token0 + function getAmount0ForLiquidity( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) internal pure returns (uint256 amount0) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + return + FullMath.mulDiv( + uint256(liquidity) << FixedPoint96.RESOLUTION, + sqrtRatioBX96 - sqrtRatioAX96, + sqrtRatioBX96 + ) / sqrtRatioAX96; + } + + /// @notice Computes the amount of token1 for a given amount of liquidity and a price range + /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary + /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary + /// @param liquidity The liquidity being valued + /// @return amount1 The amount of token1 + function getAmount1ForLiquidity( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) internal pure returns (uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); + } + + /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current + /// pool prices and the prices at the tick boundaries + /// @param sqrtRatioX96 A sqrt price representing the current pool prices + /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary + /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary + /// @param liquidity The liquidity being valued + /// @return amount0 The amount of token0 + /// @return amount1 The amount of token1 + function getAmountsForLiquidity( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) internal pure returns (uint256 amount0, uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + if (sqrtRatioX96 <= sqrtRatioAX96) { + amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + } else if (sqrtRatioX96 < sqrtRatioBX96) { + amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity); + amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity); + } else { + amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/NFTDescriptor.sol b/lib/uniswap/v3-periphery/contracts/libraries/NFTDescriptor.sol new file mode 100644 index 0000000..8414872 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/NFTDescriptor.sol @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.0; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-core/contracts/libraries/BitMath.sol'; +import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; +import '@openzeppelin/contracts/utils/Strings.sol'; +import '@openzeppelin/contracts/math/SafeMath.sol'; +import '@openzeppelin/contracts/math/SignedSafeMath.sol'; +import 'base64-sol/base64.sol'; +import './HexStrings.sol'; +import './NFTSVG.sol'; + +library NFTDescriptor { + using TickMath for int24; + using Strings for uint256; + using SafeMath for uint256; + using SafeMath for uint160; + using SafeMath for uint8; + using SignedSafeMath for int256; + using HexStrings for uint256; + + uint256 constant sqrt10X128 = 1076067327063303206878105757264492625226; + + struct ConstructTokenURIParams { + uint256 tokenId; + address quoteTokenAddress; + address baseTokenAddress; + string quoteTokenSymbol; + string baseTokenSymbol; + uint8 quoteTokenDecimals; + uint8 baseTokenDecimals; + bool flipRatio; + int24 tickLower; + int24 tickUpper; + int24 tickCurrent; + int24 tickSpacing; + uint24 fee; + address poolAddress; + } + + function constructTokenURI(ConstructTokenURIParams memory params) public pure returns (string memory) { + string memory name = generateName(params, feeToPercentString(params.fee)); + string memory descriptionPartOne = + generateDescriptionPartOne( + escapeQuotes(params.quoteTokenSymbol), + escapeQuotes(params.baseTokenSymbol), + addressToString(params.poolAddress) + ); + string memory descriptionPartTwo = + generateDescriptionPartTwo( + params.tokenId.toString(), + escapeQuotes(params.baseTokenSymbol), + addressToString(params.quoteTokenAddress), + addressToString(params.baseTokenAddress), + feeToPercentString(params.fee) + ); + string memory image = Base64.encode(bytes(generateSVGImage(params))); + + return + string( + abi.encodePacked( + 'data:application/json;base64,', + Base64.encode( + bytes( + abi.encodePacked( + '{"name":"', + name, + '", "description":"', + descriptionPartOne, + descriptionPartTwo, + '", "image": "', + 'data:image/svg+xml;base64,', + image, + '"}' + ) + ) + ) + ) + ); + } + + function escapeQuotes(string memory symbol) internal pure returns (string memory) { + bytes memory symbolBytes = bytes(symbol); + uint8 quotesCount = 0; + for (uint8 i = 0; i < symbolBytes.length; i++) { + if (symbolBytes[i] == '"') { + quotesCount++; + } + } + if (quotesCount > 0) { + bytes memory escapedBytes = new bytes(symbolBytes.length + (quotesCount)); + uint256 index; + for (uint8 i = 0; i < symbolBytes.length; i++) { + if (symbolBytes[i] == '"') { + escapedBytes[index++] = '\\'; + } + escapedBytes[index++] = symbolBytes[i]; + } + return string(escapedBytes); + } + return symbol; + } + + function generateDescriptionPartOne( + string memory quoteTokenSymbol, + string memory baseTokenSymbol, + string memory poolAddress + ) private pure returns (string memory) { + return + string( + abi.encodePacked( + 'This NFT represents a liquidity position in a Uniswap V3 ', + quoteTokenSymbol, + '-', + baseTokenSymbol, + ' pool. ', + 'The owner of this NFT can modify or redeem the position.\\n', + '\\nPool Address: ', + poolAddress, + '\\n', + quoteTokenSymbol + ) + ); + } + + function generateDescriptionPartTwo( + string memory tokenId, + string memory baseTokenSymbol, + string memory quoteTokenAddress, + string memory baseTokenAddress, + string memory feeTier + ) private pure returns (string memory) { + return + string( + abi.encodePacked( + ' Address: ', + quoteTokenAddress, + '\\n', + baseTokenSymbol, + ' Address: ', + baseTokenAddress, + '\\nFee Tier: ', + feeTier, + '\\nToken ID: ', + tokenId, + '\\n\\n', + unicode'⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure token addresses match the expected tokens, as token symbols may be imitated.' + ) + ); + } + + function generateName(ConstructTokenURIParams memory params, string memory feeTier) + private + pure + returns (string memory) + { + return + string( + abi.encodePacked( + 'Uniswap - ', + feeTier, + ' - ', + escapeQuotes(params.quoteTokenSymbol), + '/', + escapeQuotes(params.baseTokenSymbol), + ' - ', + tickToDecimalString( + !params.flipRatio ? params.tickLower : params.tickUpper, + params.tickSpacing, + params.baseTokenDecimals, + params.quoteTokenDecimals, + params.flipRatio + ), + '<>', + tickToDecimalString( + !params.flipRatio ? params.tickUpper : params.tickLower, + params.tickSpacing, + params.baseTokenDecimals, + params.quoteTokenDecimals, + params.flipRatio + ) + ) + ); + } + + struct DecimalStringParams { + // significant figures of decimal + uint256 sigfigs; + // length of decimal string + uint8 bufferLength; + // ending index for significant figures (funtion works backwards when copying sigfigs) + uint8 sigfigIndex; + // index of decimal place (0 if no decimal) + uint8 decimalIndex; + // start index for trailing/leading 0's for very small/large numbers + uint8 zerosStartIndex; + // end index for trailing/leading 0's for very small/large numbers + uint8 zerosEndIndex; + // true if decimal number is less than one + bool isLessThanOne; + // true if string should include "%" + bool isPercent; + } + + function generateDecimalString(DecimalStringParams memory params) private pure returns (string memory) { + bytes memory buffer = new bytes(params.bufferLength); + if (params.isPercent) { + buffer[buffer.length - 1] = '%'; + } + if (params.isLessThanOne) { + buffer[0] = '0'; + buffer[1] = '.'; + } + + // add leading/trailing 0's + for (uint256 zerosCursor = params.zerosStartIndex; zerosCursor < params.zerosEndIndex.add(1); zerosCursor++) { + buffer[zerosCursor] = bytes1(uint8(48)); + } + // add sigfigs + while (params.sigfigs > 0) { + if (params.decimalIndex > 0 && params.sigfigIndex == params.decimalIndex) { + buffer[params.sigfigIndex--] = '.'; + } + buffer[params.sigfigIndex--] = bytes1(uint8(uint256(48).add(params.sigfigs % 10))); + params.sigfigs /= 10; + } + return string(buffer); + } + + function tickToDecimalString( + int24 tick, + int24 tickSpacing, + uint8 baseTokenDecimals, + uint8 quoteTokenDecimals, + bool flipRatio + ) internal pure returns (string memory) { + if (tick == (TickMath.MIN_TICK / tickSpacing) * tickSpacing) { + return !flipRatio ? 'MIN' : 'MAX'; + } else if (tick == (TickMath.MAX_TICK / tickSpacing) * tickSpacing) { + return !flipRatio ? 'MAX' : 'MIN'; + } else { + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); + if (flipRatio) { + sqrtRatioX96 = uint160(uint256(1 << 192).div(sqrtRatioX96)); + } + return fixedPointToDecimalString(sqrtRatioX96, baseTokenDecimals, quoteTokenDecimals); + } + } + + function sigfigsRounded(uint256 value, uint8 digits) private pure returns (uint256, bool) { + bool extraDigit; + if (digits > 5) { + value = value.div((10**(digits - 5))); + } + bool roundUp = value % 10 > 4; + value = value.div(10); + if (roundUp) { + value = value + 1; + } + // 99999 -> 100000 gives an extra sigfig + if (value == 100000) { + value /= 10; + extraDigit = true; + } + return (value, extraDigit); + } + + function adjustForDecimalPrecision( + uint160 sqrtRatioX96, + uint8 baseTokenDecimals, + uint8 quoteTokenDecimals + ) private pure returns (uint256 adjustedSqrtRatioX96) { + uint256 difference = abs(int256(baseTokenDecimals).sub(int256(quoteTokenDecimals))); + if (difference > 0 && difference <= 18) { + if (baseTokenDecimals > quoteTokenDecimals) { + adjustedSqrtRatioX96 = sqrtRatioX96.mul(10**(difference.div(2))); + if (difference % 2 == 1) { + adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, sqrt10X128, 1 << 128); + } + } else { + adjustedSqrtRatioX96 = sqrtRatioX96.div(10**(difference.div(2))); + if (difference % 2 == 1) { + adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, 1 << 128, sqrt10X128); + } + } + } else { + adjustedSqrtRatioX96 = uint256(sqrtRatioX96); + } + } + + function abs(int256 x) private pure returns (uint256) { + return uint256(x >= 0 ? x : -x); + } + + // @notice Returns string that includes first 5 significant figures of a decimal number + // @param sqrtRatioX96 a sqrt price + function fixedPointToDecimalString( + uint160 sqrtRatioX96, + uint8 baseTokenDecimals, + uint8 quoteTokenDecimals + ) internal pure returns (string memory) { + uint256 adjustedSqrtRatioX96 = adjustForDecimalPrecision(sqrtRatioX96, baseTokenDecimals, quoteTokenDecimals); + uint256 value = FullMath.mulDiv(adjustedSqrtRatioX96, adjustedSqrtRatioX96, 1 << 64); + + bool priceBelow1 = adjustedSqrtRatioX96 < 2**96; + if (priceBelow1) { + // 10 ** 43 is precision needed to retreive 5 sigfigs of smallest possible price + 1 for rounding + value = FullMath.mulDiv(value, 10**44, 1 << 128); + } else { + // leave precision for 4 decimal places + 1 place for rounding + value = FullMath.mulDiv(value, 10**5, 1 << 128); + } + + // get digit count + uint256 temp = value; + uint8 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + // don't count extra digit kept for rounding + digits = digits - 1; + + // address rounding + (uint256 sigfigs, bool extraDigit) = sigfigsRounded(value, digits); + if (extraDigit) { + digits++; + } + + DecimalStringParams memory params; + if (priceBelow1) { + // 7 bytes ( "0." and 5 sigfigs) + leading 0's bytes + params.bufferLength = uint8(uint8(7).add(uint8(43).sub(digits))); + params.zerosStartIndex = 2; + params.zerosEndIndex = uint8(uint256(43).sub(digits).add(1)); + params.sigfigIndex = uint8(params.bufferLength.sub(1)); + } else if (digits >= 9) { + // no decimal in price string + params.bufferLength = uint8(digits.sub(4)); + params.zerosStartIndex = 5; + params.zerosEndIndex = uint8(params.bufferLength.sub(1)); + params.sigfigIndex = 4; + } else { + // 5 sigfigs surround decimal + params.bufferLength = 6; + params.sigfigIndex = 5; + params.decimalIndex = uint8(digits.sub(5).add(1)); + } + params.sigfigs = sigfigs; + params.isLessThanOne = priceBelow1; + params.isPercent = false; + + return generateDecimalString(params); + } + + // @notice Returns string as decimal percentage of fee amount. + // @param fee fee amount + function feeToPercentString(uint24 fee) internal pure returns (string memory) { + if (fee == 0) { + return '0%'; + } + uint24 temp = fee; + uint256 digits; + uint8 numSigfigs; + while (temp != 0) { + if (numSigfigs > 0) { + // count all digits preceding least significant figure + numSigfigs++; + } else if (temp % 10 != 0) { + numSigfigs++; + } + digits++; + temp /= 10; + } + + DecimalStringParams memory params; + uint256 nZeros; + if (digits >= 5) { + // if decimal > 1 (5th digit is the ones place) + uint256 decimalPlace = digits.sub(numSigfigs) >= 4 ? 0 : 1; + nZeros = digits.sub(5) < (numSigfigs.sub(1)) ? 0 : digits.sub(5).sub(numSigfigs.sub(1)); + params.zerosStartIndex = numSigfigs; + params.zerosEndIndex = uint8(params.zerosStartIndex.add(nZeros).sub(1)); + params.sigfigIndex = uint8(params.zerosStartIndex.sub(1).add(decimalPlace)); + params.bufferLength = uint8(nZeros.add(numSigfigs.add(1)).add(decimalPlace)); + } else { + // else if decimal < 1 + nZeros = uint256(5).sub(digits); + params.zerosStartIndex = 2; + params.zerosEndIndex = uint8(nZeros.add(params.zerosStartIndex).sub(1)); + params.bufferLength = uint8(nZeros.add(numSigfigs.add(2))); + params.sigfigIndex = uint8((params.bufferLength).sub(2)); + params.isLessThanOne = true; + } + params.sigfigs = uint256(fee).div(10**(digits.sub(numSigfigs))); + params.isPercent = true; + params.decimalIndex = digits > 4 ? uint8(digits.sub(4)) : 0; + + return generateDecimalString(params); + } + + function addressToString(address addr) internal pure returns (string memory) { + return (uint256(addr)).toHexString(20); + } + + function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg) { + NFTSVG.SVGParams memory svgParams = + NFTSVG.SVGParams({ + quoteToken: addressToString(params.quoteTokenAddress), + baseToken: addressToString(params.baseTokenAddress), + poolAddress: params.poolAddress, + quoteTokenSymbol: params.quoteTokenSymbol, + baseTokenSymbol: params.baseTokenSymbol, + feeTier: feeToPercentString(params.fee), + tickLower: params.tickLower, + tickUpper: params.tickUpper, + tickSpacing: params.tickSpacing, + overRange: overRange(params.tickLower, params.tickUpper, params.tickCurrent), + tokenId: params.tokenId, + color0: tokenToColorHex(uint256(params.quoteTokenAddress), 136), + color1: tokenToColorHex(uint256(params.baseTokenAddress), 136), + color2: tokenToColorHex(uint256(params.quoteTokenAddress), 0), + color3: tokenToColorHex(uint256(params.baseTokenAddress), 0), + x1: scale(getCircleCoord(uint256(params.quoteTokenAddress), 16, params.tokenId), 0, 255, 16, 274), + y1: scale(getCircleCoord(uint256(params.baseTokenAddress), 16, params.tokenId), 0, 255, 100, 484), + x2: scale(getCircleCoord(uint256(params.quoteTokenAddress), 32, params.tokenId), 0, 255, 16, 274), + y2: scale(getCircleCoord(uint256(params.baseTokenAddress), 32, params.tokenId), 0, 255, 100, 484), + x3: scale(getCircleCoord(uint256(params.quoteTokenAddress), 48, params.tokenId), 0, 255, 16, 274), + y3: scale(getCircleCoord(uint256(params.baseTokenAddress), 48, params.tokenId), 0, 255, 100, 484) + }); + + return NFTSVG.generateSVG(svgParams); + } + + function overRange( + int24 tickLower, + int24 tickUpper, + int24 tickCurrent + ) private pure returns (int8) { + if (tickCurrent < tickLower) { + return -1; + } else if (tickCurrent > tickUpper) { + return 1; + } else { + return 0; + } + } + + function scale( + uint256 n, + uint256 inMn, + uint256 inMx, + uint256 outMn, + uint256 outMx + ) private pure returns (string memory) { + return (n.sub(inMn).mul(outMx.sub(outMn)).div(inMx.sub(inMn)).add(outMn)).toString(); + } + + function tokenToColorHex(uint256 token, uint256 offset) internal pure returns (string memory str) { + return string((token >> offset).toHexStringNoPrefix(3)); + } + + function getCircleCoord( + uint256 tokenAddress, + uint256 offset, + uint256 tokenId + ) internal pure returns (uint256) { + return (sliceTokenHex(tokenAddress, offset) * tokenId) % 255; + } + + function sliceTokenHex(uint256 token, uint256 offset) internal pure returns (uint256) { + return uint256(uint8(token >> offset)); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/NFTSVG.sol b/lib/uniswap/v3-periphery/contracts/libraries/NFTSVG.sol new file mode 100644 index 0000000..2d5ce83 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/NFTSVG.sol @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.6; + +import '@openzeppelin/contracts/utils/Strings.sol'; +import '@uniswap/v3-core/contracts/libraries/BitMath.sol'; +import 'base64-sol/base64.sol'; + +/// @title NFTSVG +/// @notice Provides a function for generating an SVG associated with a Uniswap NFT +library NFTSVG { + using Strings for uint256; + + string constant curve1 = 'M1 1C41 41 105 105 145 145'; + string constant curve2 = 'M1 1C33 49 97 113 145 145'; + string constant curve3 = 'M1 1C33 57 89 113 145 145'; + string constant curve4 = 'M1 1C25 65 81 121 145 145'; + string constant curve5 = 'M1 1C17 73 73 129 145 145'; + string constant curve6 = 'M1 1C9 81 65 137 145 145'; + string constant curve7 = 'M1 1C1 89 57.5 145 145 145'; + string constant curve8 = 'M1 1C1 97 49 145 145 145'; + + struct SVGParams { + string quoteToken; + string baseToken; + address poolAddress; + string quoteTokenSymbol; + string baseTokenSymbol; + string feeTier; + int24 tickLower; + int24 tickUpper; + int24 tickSpacing; + int8 overRange; + uint256 tokenId; + string color0; + string color1; + string color2; + string color3; + string x1; + string y1; + string x2; + string y2; + string x3; + string y3; + } + + function generateSVG(SVGParams memory params) internal pure returns (string memory svg) { + /* + address: "0xe8ab59d3bcde16a29912de83a90eb39628cfc163", + msg: "Forged in SVG for Uniswap in 2021 by 0xe8ab59d3bcde16a29912de83a90eb39628cfc163", + sig: "0x2df0e99d9cbfec33a705d83f75666d98b22dea7c1af412c584f7d626d83f02875993df740dc87563b9c73378f8462426da572d7989de88079a382ad96c57b68d1b", + version: "2" + */ + return + string( + abi.encodePacked( + generateSVGDefs(params), + generateSVGBorderText( + params.quoteToken, + params.baseToken, + params.quoteTokenSymbol, + params.baseTokenSymbol + ), + generateSVGCardMantle(params.quoteTokenSymbol, params.baseTokenSymbol, params.feeTier), + generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange), + generateSVGPositionDataAndLocationCurve( + params.tokenId.toString(), + params.tickLower, + params.tickUpper + ), + generateSVGRareSparkle(params.tokenId, params.poolAddress), + '' + ) + ); + } + + function generateSVGDefs(SVGParams memory params) private pure returns (string memory svg) { + svg = string( + abi.encodePacked( + '", + '', + '" + ) + ) + ), + '"/>" + ) + ) + ), + '"/>" + ) + ) + ), + '" />', + '" + ) + ) + ), + '" /> ', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + ' ', + '', + '', + '' + ) + ); + } + + function generateSVGBorderText( + string memory quoteToken, + string memory baseToken, + string memory quoteTokenSymbol, + string memory baseTokenSymbol + ) private pure returns (string memory svg) { + svg = string( + abi.encodePacked( + '', + '', + baseToken, + unicode' • ', + baseTokenSymbol, + ' ', + ' ', + baseToken, + unicode' • ', + baseTokenSymbol, + ' ', + '', + quoteToken, + unicode' • ', + quoteTokenSymbol, + ' ', + quoteToken, + unicode' • ', + quoteTokenSymbol, + ' ' + ) + ); + } + + function generateSVGCardMantle( + string memory quoteTokenSymbol, + string memory baseTokenSymbol, + string memory feeTier + ) private pure returns (string memory svg) { + svg = string( + abi.encodePacked( + ' ', + quoteTokenSymbol, + '/', + baseTokenSymbol, + '', + feeTier, + '', + '' + ) + ); + } + + function generageSvgCurve( + int24 tickLower, + int24 tickUpper, + int24 tickSpacing, + int8 overRange + ) private pure returns (string memory svg) { + string memory fade = overRange == 1 ? '#fade-up' : overRange == -1 ? '#fade-down' : '#none'; + string memory curve = getCurve(tickLower, tickUpper, tickSpacing); + svg = string( + abi.encodePacked( + '' + '' + '', + '', + '', + '', + generateSVGCurveCircle(overRange) + ) + ); + } + + function getCurve( + int24 tickLower, + int24 tickUpper, + int24 tickSpacing + ) internal pure returns (string memory curve) { + int24 tickRange = (tickUpper - tickLower) / tickSpacing; + if (tickRange <= 4) { + curve = curve1; + } else if (tickRange <= 8) { + curve = curve2; + } else if (tickRange <= 16) { + curve = curve3; + } else if (tickRange <= 32) { + curve = curve4; + } else if (tickRange <= 64) { + curve = curve5; + } else if (tickRange <= 128) { + curve = curve6; + } else if (tickRange <= 256) { + curve = curve7; + } else { + curve = curve8; + } + } + + function generateSVGCurveCircle(int8 overRange) internal pure returns (string memory svg) { + string memory curvex1 = '73'; + string memory curvey1 = '190'; + string memory curvex2 = '217'; + string memory curvey2 = '334'; + if (overRange == 1 || overRange == -1) { + svg = string( + abi.encodePacked( + '' + ) + ); + } else { + svg = string( + abi.encodePacked( + '', + '' + ) + ); + } + } + + function generateSVGPositionDataAndLocationCurve( + string memory tokenId, + int24 tickLower, + int24 tickUpper + ) private pure returns (string memory svg) { + string memory tickLowerStr = tickToString(tickLower); + string memory tickUpperStr = tickToString(tickUpper); + uint256 str1length = bytes(tokenId).length + 4; + uint256 str2length = bytes(tickLowerStr).length + 10; + uint256 str3length = bytes(tickUpperStr).length + 10; + (string memory xCoord, string memory yCoord) = rangeLocation(tickLower, tickUpper); + svg = string( + abi.encodePacked( + ' ', + '', + 'ID: ', + tokenId, + '', + ' ', + '', + 'Min Tick: ', + tickLowerStr, + '', + ' ', + '', + 'Max Tick: ', + tickUpperStr, + '' + '', + '', + '', + '' + ) + ); + } + + function tickToString(int24 tick) private pure returns (string memory) { + string memory sign = ''; + if (tick < 0) { + tick = tick * -1; + sign = '-'; + } + return string(abi.encodePacked(sign, uint256(tick).toString())); + } + + function rangeLocation(int24 tickLower, int24 tickUpper) internal pure returns (string memory, string memory) { + int24 midPoint = (tickLower + tickUpper) / 2; + if (midPoint < -125_000) { + return ('8', '7'); + } else if (midPoint < -75_000) { + return ('8', '10.5'); + } else if (midPoint < -25_000) { + return ('8', '14.25'); + } else if (midPoint < -5_000) { + return ('10', '18'); + } else if (midPoint < 0) { + return ('11', '21'); + } else if (midPoint < 5_000) { + return ('13', '23'); + } else if (midPoint < 25_000) { + return ('15', '25'); + } else if (midPoint < 75_000) { + return ('18', '26'); + } else if (midPoint < 125_000) { + return ('21', '27'); + } else { + return ('24', '27'); + } + } + + function generateSVGRareSparkle(uint256 tokenId, address poolAddress) private pure returns (string memory svg) { + if (isRare(tokenId, poolAddress)) { + svg = string( + abi.encodePacked( + '', + '', + '' + ) + ); + } else { + svg = ''; + } + } + + function isRare(uint256 tokenId, address poolAddress) internal pure returns (bool) { + bytes32 h = keccak256(abi.encodePacked(tokenId, poolAddress)); + return uint256(h) < type(uint256).max / (1 + BitMath.mostSignificantBit(tokenId) * 2); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol b/lib/uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol new file mode 100644 index 0000000..ddb82be --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0 <0.8.0; + +import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +/// @title Oracle library +/// @notice Provides functions to integrate with V3 pool oracle +library OracleLibrary { + /// @notice Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool + /// @param pool Address of the pool that we want to observe + /// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means + /// @return arithmeticMeanTick The arithmetic mean tick from (block.timestamp - secondsAgo) to block.timestamp + /// @return harmonicMeanLiquidity The harmonic mean liquidity from (block.timestamp - secondsAgo) to block.timestamp + function consult(address pool, uint32 secondsAgo) + internal + view + returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity) + { + require(secondsAgo != 0, 'BP'); + + uint32[] memory secondsAgos = new uint32[](2); + secondsAgos[0] = secondsAgo; + secondsAgos[1] = 0; + + (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = + IUniswapV3Pool(pool).observe(secondsAgos); + + int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; + uint160 secondsPerLiquidityCumulativesDelta = + secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0]; + + arithmeticMeanTick = int24(tickCumulativesDelta / secondsAgo); + // Always round to negative infinity + if (tickCumulativesDelta < 0 && (tickCumulativesDelta % secondsAgo != 0)) arithmeticMeanTick--; + + // We are multiplying here instead of shifting to ensure that harmonicMeanLiquidity doesn't overflow uint128 + uint192 secondsAgoX160 = uint192(secondsAgo) * type(uint160).max; + harmonicMeanLiquidity = uint128(secondsAgoX160 / (uint192(secondsPerLiquidityCumulativesDelta) << 32)); + } + + /// @notice Given a tick and a token amount, calculates the amount of token received in exchange + /// @param tick Tick value used to calculate the quote + /// @param baseAmount Amount of token to be converted + /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination + /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination + /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken + function getQuoteAtTick( + int24 tick, + uint128 baseAmount, + address baseToken, + address quoteToken + ) internal pure returns (uint256 quoteAmount) { + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); + + // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself + if (sqrtRatioX96 <= type(uint128).max) { + uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; + quoteAmount = baseToken < quoteToken + ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) + : FullMath.mulDiv(1 << 192, baseAmount, ratioX192); + } else { + uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); + quoteAmount = baseToken < quoteToken + ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) + : FullMath.mulDiv(1 << 128, baseAmount, ratioX128); + } + } + + /// @notice Given a pool, it returns the number of seconds ago of the oldest stored observation + /// @param pool Address of Uniswap V3 pool that we want to observe + /// @return secondsAgo The number of seconds ago of the oldest observation stored for the pool + function getOldestObservationSecondsAgo(address pool) internal view returns (uint32 secondsAgo) { + (, , uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); + require(observationCardinality > 0, 'NI'); + + (uint32 observationTimestamp, , , bool initialized) = + IUniswapV3Pool(pool).observations((observationIndex + 1) % observationCardinality); + + // The next index might not be initialized if the cardinality is in the process of increasing + // In this case the oldest observation is always in index 0 + if (!initialized) { + (observationTimestamp, , , ) = IUniswapV3Pool(pool).observations(0); + } + + secondsAgo = uint32(block.timestamp) - observationTimestamp; + } + + /// @notice Given a pool, it returns the tick value as of the start of the current block + /// @param pool Address of Uniswap V3 pool + /// @return The tick that the pool was in at the start of the current block + function getBlockStartingTickAndLiquidity(address pool) internal view returns (int24, uint128) { + (, int24 tick, uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); + + // 2 observations are needed to reliably calculate the block starting tick + require(observationCardinality > 1, 'NEO'); + + // If the latest observation occurred in the past, then no tick-changing trades have happened in this block + // therefore the tick in `slot0` is the same as at the beginning of the current block. + // We don't need to check if this observation is initialized - it is guaranteed to be. + (uint32 observationTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, ) = + IUniswapV3Pool(pool).observations(observationIndex); + if (observationTimestamp != uint32(block.timestamp)) { + return (tick, IUniswapV3Pool(pool).liquidity()); + } + + uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality; + ( + uint32 prevObservationTimestamp, + int56 prevTickCumulative, + uint160 prevSecondsPerLiquidityCumulativeX128, + bool prevInitialized + ) = IUniswapV3Pool(pool).observations(prevIndex); + + require(prevInitialized, 'ONI'); + + uint32 delta = observationTimestamp - prevObservationTimestamp; + tick = int24((tickCumulative - prevTickCumulative) / delta); + uint128 liquidity = + uint128( + (uint192(delta) * type(uint160).max) / + (uint192(secondsPerLiquidityCumulativeX128 - prevSecondsPerLiquidityCumulativeX128) << 32) + ); + return (tick, liquidity); + } + + /// @notice Information for calculating a weighted arithmetic mean tick + struct WeightedTickData { + int24 tick; + uint128 weight; + } + + /// @notice Given an array of ticks and weights, calculates the weighted arithmetic mean tick + /// @param weightedTickData An array of ticks and weights + /// @return weightedArithmeticMeanTick The weighted arithmetic mean tick + /// @dev Each entry of `weightedTickData` should represents ticks from pools with the same underlying pool tokens. If they do not, + /// extreme care must be taken to ensure that ticks are comparable (including decimal differences). + /// @dev Note that the weighted arithmetic mean tick corresponds to the weighted geometric mean price. + function getWeightedArithmeticMeanTick(WeightedTickData[] memory weightedTickData) + internal + pure + returns (int24 weightedArithmeticMeanTick) + { + // Accumulates the sum of products between each tick and its weight + int256 numerator; + + // Accumulates the sum of the weights + uint256 denominator; + + // Products fit in 152 bits, so it would take an array of length ~2**104 to overflow this logic + for (uint256 i; i < weightedTickData.length; i++) { + numerator += weightedTickData[i].tick * int256(weightedTickData[i].weight); + denominator += weightedTickData[i].weight; + } + + weightedArithmeticMeanTick = int24(numerator / int256(denominator)); + // Always round to negative infinity + if (numerator < 0 && (numerator % int256(denominator) != 0)) weightedArithmeticMeanTick--; + } + + /// @notice Returns the "synthetic" tick which represents the price of the first entry in `tokens` in terms of the last + /// @dev Useful for calculating relative prices along routes. + /// @dev There must be one tick for each pairwise set of tokens. + /// @param tokens The token contract addresses + /// @param ticks The ticks, representing the price of each token pair in `tokens` + /// @return syntheticTick The synthetic tick, representing the relative price of the outermost tokens in `tokens` + function getChainedPrice(address[] memory tokens, int24[] memory ticks) + internal + pure + returns (int256 syntheticTick) + { + require(tokens.length - 1 == ticks.length, 'DL'); + for (uint256 i = 1; i <= ticks.length; i++) { + // check the tokens for address sort order, then accumulate the + // ticks into the running synthetic tick, ensuring that intermediate tokens "cancel out" + tokens[i - 1] < tokens[i] ? syntheticTick += ticks[i - 1] : syntheticTick -= ticks[i - 1]; + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/Path.sol b/lib/uniswap/v3-periphery/contracts/libraries/Path.sol new file mode 100644 index 0000000..d6ee864 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/Path.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import './BytesLib.sol'; + +/// @title Functions for manipulating path data for multihop swaps +library Path { + using BytesLib for bytes; + + /// @dev The length of the bytes encoded address + uint256 private constant ADDR_SIZE = 20; + /// @dev The length of the bytes encoded fee + uint256 private constant FEE_SIZE = 3; + + /// @dev The offset of a single token address and pool fee + uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; + /// @dev The offset of an encoded pool key + uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE; + /// @dev The minimum length of an encoding that contains 2 or more pools + uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET; + + /// @notice Returns true iff the path contains two or more pools + /// @param path The encoded swap path + /// @return True if path contains two or more pools, otherwise false + function hasMultiplePools(bytes memory path) internal pure returns (bool) { + return path.length >= MULTIPLE_POOLS_MIN_LENGTH; + } + + /// @notice Returns the number of pools in the path + /// @param path The encoded swap path + /// @return The number of pools in the path + function numPools(bytes memory path) internal pure returns (uint256) { + // Ignore the first token address. From then on every fee and token offset indicates a pool. + return ((path.length - ADDR_SIZE) / NEXT_OFFSET); + } + + /// @notice Decodes the first pool in path + /// @param path The bytes encoded swap path + /// @return tokenA The first token of the given pool + /// @return tokenB The second token of the given pool + /// @return fee The fee level of the pool + function decodeFirstPool(bytes memory path) + internal + pure + returns ( + address tokenA, + address tokenB, + uint24 fee + ) + { + tokenA = path.toAddress(0); + fee = path.toUint24(ADDR_SIZE); + tokenB = path.toAddress(NEXT_OFFSET); + } + + /// @notice Gets the segment corresponding to the first pool in the path + /// @param path The bytes encoded swap path + /// @return The segment containing all data necessary to target the first pool in the path + function getFirstPool(bytes memory path) internal pure returns (bytes memory) { + return path.slice(0, POP_OFFSET); + } + + /// @notice Skips a token + fee element from the buffer and returns the remainder + /// @param path The swap path + /// @return The remaining token + fee elements in the path + function skipToken(bytes memory path) internal pure returns (bytes memory) { + return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/PoolAddress.sol b/lib/uniswap/v3-periphery/contracts/libraries/PoolAddress.sol new file mode 100644 index 0000000..60de385 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/PoolAddress.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee +library PoolAddress { + bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; + + /// @notice The identifying key of the pool + struct PoolKey { + address token0; + address token1; + uint24 fee; + } + + /// @notice Returns PoolKey: the ordered tokens with the matched fee levels + /// @param tokenA The first token of a pool, unsorted + /// @param tokenB The second token of a pool, unsorted + /// @param fee The fee level of the pool + /// @return Poolkey The pool details with ordered token0 and token1 assignments + function getPoolKey( + address tokenA, + address tokenB, + uint24 fee + ) internal pure returns (PoolKey memory) { + if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA); + return PoolKey({token0: tokenA, token1: tokenB, fee: fee}); + } + + /// @notice Deterministically computes the pool address given the factory and PoolKey + /// @param factory The Uniswap V3 factory contract address + /// @param key The PoolKey + /// @return pool The contract address of the V3 pool + function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) { + require(key.token0 < key.token1); + pool = address( + uint256( + keccak256( + abi.encodePacked( + hex'ff', + factory, + keccak256(abi.encode(key.token0, key.token1, key.fee)), + POOL_INIT_CODE_HASH + ) + ) + ) + ); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/PoolTicksCounter.sol b/lib/uniswap/v3-periphery/contracts/libraries/PoolTicksCounter.sol new file mode 100644 index 0000000..fb0682f --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/PoolTicksCounter.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +library PoolTicksCounter { + /// @dev This function counts the number of initialized ticks that would incur a gas cost between tickBefore and tickAfter. + /// When tickBefore and/or tickAfter themselves are initialized, the logic over whether we should count them depends on the + /// direction of the swap. If we are swapping upwards (tickAfter > tickBefore) we don't want to count tickBefore but we do + /// want to count tickAfter. The opposite is true if we are swapping downwards. + function countInitializedTicksCrossed( + IUniswapV3Pool self, + int24 tickBefore, + int24 tickAfter + ) internal view returns (uint32 initializedTicksCrossed) { + int16 wordPosLower; + int16 wordPosHigher; + uint8 bitPosLower; + uint8 bitPosHigher; + bool tickBeforeInitialized; + bool tickAfterInitialized; + + { + // Get the key and offset in the tick bitmap of the active tick before and after the swap. + int16 wordPos = int16((tickBefore / self.tickSpacing()) >> 8); + uint8 bitPos = uint8((tickBefore / self.tickSpacing()) % 256); + + int16 wordPosAfter = int16((tickAfter / self.tickSpacing()) >> 8); + uint8 bitPosAfter = uint8((tickAfter / self.tickSpacing()) % 256); + + // In the case where tickAfter is initialized, we only want to count it if we are swapping downwards. + // If the initializable tick after the swap is initialized, our original tickAfter is a + // multiple of tick spacing, and we are swapping downwards we know that tickAfter is initialized + // and we shouldn't count it. + tickAfterInitialized = + ((self.tickBitmap(wordPosAfter) & (1 << bitPosAfter)) > 0) && + ((tickAfter % self.tickSpacing()) == 0) && + (tickBefore > tickAfter); + + // In the case where tickBefore is initialized, we only want to count it if we are swapping upwards. + // Use the same logic as above to decide whether we should count tickBefore or not. + tickBeforeInitialized = + ((self.tickBitmap(wordPos) & (1 << bitPos)) > 0) && + ((tickBefore % self.tickSpacing()) == 0) && + (tickBefore < tickAfter); + + if (wordPos < wordPosAfter || (wordPos == wordPosAfter && bitPos <= bitPosAfter)) { + wordPosLower = wordPos; + bitPosLower = bitPos; + wordPosHigher = wordPosAfter; + bitPosHigher = bitPosAfter; + } else { + wordPosLower = wordPosAfter; + bitPosLower = bitPosAfter; + wordPosHigher = wordPos; + bitPosHigher = bitPos; + } + } + + // Count the number of initialized ticks crossed by iterating through the tick bitmap. + // Our first mask should include the lower tick and everything to its left. + uint256 mask = type(uint256).max << bitPosLower; + while (wordPosLower <= wordPosHigher) { + // If we're on the final tick bitmap page, ensure we only count up to our + // ending tick. + if (wordPosLower == wordPosHigher) { + mask = mask & (type(uint256).max >> (255 - bitPosHigher)); + } + + uint256 masked = self.tickBitmap(wordPosLower) & mask; + initializedTicksCrossed += countOneBits(masked); + wordPosLower++; + // Reset our mask so we consider all bits on the next iteration. + mask = type(uint256).max; + } + + if (tickAfterInitialized) { + initializedTicksCrossed -= 1; + } + + if (tickBeforeInitialized) { + initializedTicksCrossed -= 1; + } + + return initializedTicksCrossed; + } + + function countOneBits(uint256 x) private pure returns (uint16) { + uint16 bits = 0; + while (x != 0) { + bits++; + x &= (x - 1); + } + return bits; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/PositionKey.sol b/lib/uniswap/v3-periphery/contracts/libraries/PositionKey.sol new file mode 100644 index 0000000..a60fc18 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/PositionKey.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +library PositionKey { + /// @dev Returns the key of the position in the core library + function compute( + address owner, + int24 tickLower, + int24 tickUpper + ) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(owner, tickLower, tickUpper)); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/PositionValue.sol b/lib/uniswap/v3-periphery/contracts/libraries/PositionValue.sol new file mode 100644 index 0000000..265fc04 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/PositionValue.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.8 <0.8.0; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@uniswap/v3-core/contracts/libraries/FixedPoint128.sol'; +import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; +import '@uniswap/v3-core/contracts/libraries/Tick.sol'; +import '../interfaces/INonfungiblePositionManager.sol'; +import './LiquidityAmounts.sol'; +import './PoolAddress.sol'; +import './PositionKey.sol'; + +/// @title Returns information about the token value held in a Uniswap V3 NFT +library PositionValue { + /// @notice Returns the total amounts of token0 and token1, i.e. the sum of fees and principal + /// that a given nonfungible position manager token is worth + /// @param positionManager The Uniswap V3 NonfungiblePositionManager + /// @param tokenId The tokenId of the token for which to get the total value + /// @param sqrtRatioX96 The square root price X96 for which to calculate the principal amounts + /// @return amount0 The total amount of token0 including principal and fees + /// @return amount1 The total amount of token1 including principal and fees + function total( + INonfungiblePositionManager positionManager, + uint256 tokenId, + uint160 sqrtRatioX96 + ) internal view returns (uint256 amount0, uint256 amount1) { + (uint256 amount0Principal, uint256 amount1Principal) = principal(positionManager, tokenId, sqrtRatioX96); + (uint256 amount0Fee, uint256 amount1Fee) = fees(positionManager, tokenId); + return (amount0Principal + amount0Fee, amount1Principal + amount1Fee); + } + + /// @notice Calculates the principal (currently acting as liquidity) owed to the token owner in the event + /// that the position is burned + /// @param positionManager The Uniswap V3 NonfungiblePositionManager + /// @param tokenId The tokenId of the token for which to get the total principal owed + /// @param sqrtRatioX96 The square root price X96 for which to calculate the principal amounts + /// @return amount0 The principal amount of token0 + /// @return amount1 The principal amount of token1 + function principal( + INonfungiblePositionManager positionManager, + uint256 tokenId, + uint160 sqrtRatioX96 + ) internal view returns (uint256 amount0, uint256 amount1) { + (, , , , , int24 tickLower, int24 tickUpper, uint128 liquidity, , , , ) = positionManager.positions(tokenId); + + return + LiquidityAmounts.getAmountsForLiquidity( + sqrtRatioX96, + TickMath.getSqrtRatioAtTick(tickLower), + TickMath.getSqrtRatioAtTick(tickUpper), + liquidity + ); + } + + struct FeeParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint128 liquidity; + uint256 positionFeeGrowthInside0LastX128; + uint256 positionFeeGrowthInside1LastX128; + uint256 tokensOwed0; + uint256 tokensOwed1; + } + + /// @notice Calculates the total fees owed to the token owner + /// @param positionManager The Uniswap V3 NonfungiblePositionManager + /// @param tokenId The tokenId of the token for which to get the total fees owed + /// @return amount0 The amount of fees owed in token0 + /// @return amount1 The amount of fees owed in token1 + function fees(INonfungiblePositionManager positionManager, uint256 tokenId) + internal + view + returns (uint256 amount0, uint256 amount1) + { + ( + , + , + address token0, + address token1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 positionFeeGrowthInside0LastX128, + uint256 positionFeeGrowthInside1LastX128, + uint256 tokensOwed0, + uint256 tokensOwed1 + ) = positionManager.positions(tokenId); + + return + _fees( + positionManager, + FeeParams({ + token0: token0, + token1: token1, + fee: fee, + tickLower: tickLower, + tickUpper: tickUpper, + liquidity: liquidity, + positionFeeGrowthInside0LastX128: positionFeeGrowthInside0LastX128, + positionFeeGrowthInside1LastX128: positionFeeGrowthInside1LastX128, + tokensOwed0: tokensOwed0, + tokensOwed1: tokensOwed1 + }) + ); + } + + function _fees(INonfungiblePositionManager positionManager, FeeParams memory feeParams) + private + view + returns (uint256 amount0, uint256 amount1) + { + (uint256 poolFeeGrowthInside0LastX128, uint256 poolFeeGrowthInside1LastX128) = + _getFeeGrowthInside( + IUniswapV3Pool( + PoolAddress.computeAddress( + positionManager.factory(), + PoolAddress.PoolKey({token0: feeParams.token0, token1: feeParams.token1, fee: feeParams.fee}) + ) + ), + feeParams.tickLower, + feeParams.tickUpper + ); + + amount0 = + FullMath.mulDiv( + poolFeeGrowthInside0LastX128 - feeParams.positionFeeGrowthInside0LastX128, + feeParams.liquidity, + FixedPoint128.Q128 + ) + + feeParams.tokensOwed0; + + amount1 = + FullMath.mulDiv( + poolFeeGrowthInside1LastX128 - feeParams.positionFeeGrowthInside1LastX128, + feeParams.liquidity, + FixedPoint128.Q128 + ) + + feeParams.tokensOwed1; + } + + function _getFeeGrowthInside( + IUniswapV3Pool pool, + int24 tickLower, + int24 tickUpper + ) private view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) { + (, int24 tickCurrent, , , , , ) = pool.slot0(); + (, , uint256 lowerFeeGrowthOutside0X128, uint256 lowerFeeGrowthOutside1X128, , , , ) = pool.ticks(tickLower); + (, , uint256 upperFeeGrowthOutside0X128, uint256 upperFeeGrowthOutside1X128, , , , ) = pool.ticks(tickUpper); + + if (tickCurrent < tickLower) { + feeGrowthInside0X128 = lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128; + feeGrowthInside1X128 = lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128; + } else if (tickCurrent < tickUpper) { + uint256 feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128(); + uint256 feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128(); + feeGrowthInside0X128 = feeGrowthGlobal0X128 - lowerFeeGrowthOutside0X128 - upperFeeGrowthOutside0X128; + feeGrowthInside1X128 = feeGrowthGlobal1X128 - lowerFeeGrowthOutside1X128 - upperFeeGrowthOutside1X128; + } else { + feeGrowthInside0X128 = upperFeeGrowthOutside0X128 - lowerFeeGrowthOutside0X128; + feeGrowthInside1X128 = upperFeeGrowthOutside1X128 - lowerFeeGrowthOutside1X128; + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/SqrtPriceMathPartial.sol b/lib/uniswap/v3-periphery/contracts/libraries/SqrtPriceMathPartial.sol new file mode 100644 index 0000000..bb8c769 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/SqrtPriceMathPartial.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; +import '@uniswap/v3-core/contracts/libraries/UnsafeMath.sol'; +import '@uniswap/v3-core/contracts/libraries/FixedPoint96.sol'; + +/// @title Functions based on Q64.96 sqrt price and liquidity +/// @notice Exposes two functions from @uniswap/v3-core SqrtPriceMath +/// that use square root of price as a Q64.96 and liquidity to compute deltas +library SqrtPriceMathPartial { + /// @notice Gets the amount0 delta between two prices + /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper), + /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up or down + /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices + function getAmount0Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount0) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; + uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; + + require(sqrtRatioAX96 > 0); + + return + roundUp + ? UnsafeMath.divRoundingUp( + FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), + sqrtRatioAX96 + ) + : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96; + } + + /// @notice Gets the amount1 delta between two prices + /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower)) + /// @param sqrtRatioAX96 A sqrt price + /// @param sqrtRatioBX96 Another sqrt price + /// @param liquidity The amount of usable liquidity + /// @param roundUp Whether to round the amount up, or down + /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices + function getAmount1Delta( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity, + bool roundUp + ) internal pure returns (uint256 amount1) { + if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96); + + return + roundUp + ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96) + : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/TokenRatioSortOrder.sol b/lib/uniswap/v3-periphery/contracts/libraries/TokenRatioSortOrder.sol new file mode 100644 index 0000000..d9a2c9e --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/TokenRatioSortOrder.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6; + +library TokenRatioSortOrder { + int256 constant NUMERATOR_MOST = 300; + int256 constant NUMERATOR_MORE = 200; + int256 constant NUMERATOR = 100; + + int256 constant DENOMINATOR_MOST = -300; + int256 constant DENOMINATOR_MORE = -200; + int256 constant DENOMINATOR = -100; +} diff --git a/lib/uniswap/v3-periphery/contracts/libraries/TransferHelper.sol b/lib/uniswap/v3-periphery/contracts/libraries/TransferHelper.sol new file mode 100644 index 0000000..7fc2797 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/libraries/TransferHelper.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.6.0; + +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +library TransferHelper { + /// @notice Transfers tokens from the targeted address to the given destination + /// @notice Errors with 'STF' if transfer fails + /// @param token The contract address of the token to be transferred + /// @param from The originating address from which the tokens will be transferred + /// @param to The destination address of the transfer + /// @param value The amount to be transferred + function safeTransferFrom( + address token, + address from, + address to, + uint256 value + ) internal { + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF'); + } + + /// @notice Transfers tokens from msg.sender to a recipient + /// @dev Errors with ST if transfer fails + /// @param token The contract address of the token which will be transferred + /// @param to The recipient of the transfer + /// @param value The value of the transfer + function safeTransfer( + address token, + address to, + uint256 value + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST'); + } + + /// @notice Approves the stipulated contract to spend the given allowance in the given token + /// @dev Errors with 'SA' if transfer fails + /// @param token The contract address of the token to be approved + /// @param to The target of the approval + /// @param value The amount of the given token the target will be allowed to spend + function safeApprove( + address token, + address to, + uint256 value + ) internal { + (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA'); + } + + /// @notice Transfers ETH to the recipient address + /// @dev Fails with `STE` + /// @param to The destination of the transfer + /// @param value The value to be transferred + function safeTransferETH(address to, uint256 value) internal { + (bool success, ) = to.call{value: value}(new bytes(0)); + require(success, 'STE'); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/Base64Test.sol b/lib/uniswap/v3-periphery/contracts/test/Base64Test.sol new file mode 100644 index 0000000..cb66a87 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/Base64Test.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import 'base64-sol/base64.sol'; + +contract Base64Test { + function encode(bytes memory data) external pure returns (string memory) { + return Base64.encode(data); + } + + function getGasCostOfEncode(bytes memory data) external view returns (uint256) { + uint256 gasBefore = gasleft(); + Base64.encode(data); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/LiquidityAmountsTest.sol b/lib/uniswap/v3-periphery/contracts/test/LiquidityAmountsTest.sol new file mode 100644 index 0000000..dad61c0 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/LiquidityAmountsTest.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/LiquidityAmounts.sol'; + +contract LiquidityAmountsTest { + function getLiquidityForAmount0( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount0 + ) external pure returns (uint128 liquidity) { + return LiquidityAmounts.getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0); + } + + function getGasCostOfGetLiquidityForAmount0( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount0 + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityAmounts.getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0); + return gasBefore - gasleft(); + } + + function getLiquidityForAmount1( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount1 + ) external pure returns (uint128 liquidity) { + return LiquidityAmounts.getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1); + } + + function getGasCostOfGetLiquidityForAmount1( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount1 + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityAmounts.getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1); + return gasBefore - gasleft(); + } + + function getLiquidityForAmounts( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount0, + uint256 amount1 + ) external pure returns (uint128 liquidity) { + return LiquidityAmounts.getLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1); + } + + function getGasCostOfGetLiquidityForAmounts( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint256 amount0, + uint256 amount1 + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityAmounts.getLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1); + return gasBefore - gasleft(); + } + + function getAmount0ForLiquidity( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) external pure returns (uint256 amount0) { + return LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + } + + function getGasCostOfGetAmount0ForLiquidity( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityAmounts.getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + return gasBefore - gasleft(); + } + + function getAmount1ForLiquidity( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) external pure returns (uint256 amount1) { + return LiquidityAmounts.getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity); + } + + function getGasCostOfGetAmount1ForLiquidity( + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityAmounts.getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, liquidity); + return gasBefore - gasleft(); + } + + function getAmountsForLiquidity( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) external pure returns (uint256 amount0, uint256 amount1) { + return LiquidityAmounts.getAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity); + } + + function getGasCostOfGetAmountsForLiquidity( + uint160 sqrtRatioX96, + uint160 sqrtRatioAX96, + uint160 sqrtRatioBX96, + uint128 liquidity + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + LiquidityAmounts.getAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/MockObservable.sol b/lib/uniswap/v3-periphery/contracts/test/MockObservable.sol new file mode 100644 index 0000000..19e8674 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/MockObservable.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +contract MockObservable { + Observation private observation0; + Observation private observation1; + + struct Observation { + uint32 secondsAgo; + int56 tickCumulatives; + uint160 secondsPerLiquidityCumulativeX128s; + } + + constructor( + uint32[] memory secondsAgos, + int56[] memory tickCumulatives, + uint160[] memory secondsPerLiquidityCumulativeX128s + ) { + require( + secondsAgos.length == 2 && tickCumulatives.length == 2 && secondsPerLiquidityCumulativeX128s.length == 2, + 'Invalid test case size' + ); + + observation0 = Observation(secondsAgos[0], tickCumulatives[0], secondsPerLiquidityCumulativeX128s[0]); + observation1 = Observation(secondsAgos[1], tickCumulatives[1], secondsPerLiquidityCumulativeX128s[1]); + } + + function observe(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) + { + require( + secondsAgos[0] == observation0.secondsAgo && secondsAgos[1] == observation1.secondsAgo, + 'Invalid test case' + ); + + int56[] memory _tickCumulatives = new int56[](2); + _tickCumulatives[0] = observation0.tickCumulatives; + _tickCumulatives[1] = observation1.tickCumulatives; + + uint160[] memory _secondsPerLiquidityCumulativeX128s = new uint160[](2); + _secondsPerLiquidityCumulativeX128s[0] = observation0.secondsPerLiquidityCumulativeX128s; + _secondsPerLiquidityCumulativeX128s[1] = observation1.secondsPerLiquidityCumulativeX128s; + + return (_tickCumulatives, _secondsPerLiquidityCumulativeX128s); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/MockObservations.sol b/lib/uniswap/v3-periphery/contracts/test/MockObservations.sol new file mode 100644 index 0000000..81b5c75 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/MockObservations.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '@uniswap/v3-core/contracts/libraries/Oracle.sol'; + +contract MockObservations { + Oracle.Observation[4] internal oracleObservations; + + int24 slot0Tick; + uint16 internal slot0ObservationCardinality; + uint16 internal slot0ObservationIndex; + uint128 public liquidity; + + bool internal lastObservationCurrentTimestamp; + + constructor( + uint32[4] memory _blockTimestamps, + int56[4] memory _tickCumulatives, + uint128[4] memory _secondsPerLiquidityCumulativeX128s, + bool[4] memory _initializeds, + int24 _tick, + uint16 _observationCardinality, + uint16 _observationIndex, + bool _lastObservationCurrentTimestamp, + uint128 _liquidity + ) { + for (uint256 i = 0; i < _blockTimestamps.length; i++) { + oracleObservations[i] = Oracle.Observation({ + blockTimestamp: _blockTimestamps[i], + tickCumulative: _tickCumulatives[i], + secondsPerLiquidityCumulativeX128: _secondsPerLiquidityCumulativeX128s[i], + initialized: _initializeds[i] + }); + } + + slot0Tick = _tick; + slot0ObservationCardinality = _observationCardinality; + slot0ObservationIndex = _observationIndex; + lastObservationCurrentTimestamp = _lastObservationCurrentTimestamp; + liquidity = _liquidity; + } + + function slot0() + external + view + returns ( + uint160, + int24, + uint16, + uint16, + uint16, + uint8, + bool + ) + { + return (0, slot0Tick, slot0ObservationIndex, slot0ObservationCardinality, 0, 0, false); + } + + function observations(uint256 index) + external + view + returns ( + uint32, + int56, + uint160, + bool + ) + { + Oracle.Observation memory observation = oracleObservations[index]; + if (lastObservationCurrentTimestamp) { + observation.blockTimestamp = + uint32(block.timestamp) - + (oracleObservations[slot0ObservationIndex].blockTimestamp - observation.blockTimestamp); + } + return ( + observation.blockTimestamp, + observation.tickCumulative, + observation.secondsPerLiquidityCumulativeX128, + observation.initialized + ); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/MockTimeNonfungiblePositionManager.sol b/lib/uniswap/v3-periphery/contracts/test/MockTimeNonfungiblePositionManager.sol new file mode 100644 index 0000000..4b1c1f9 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/MockTimeNonfungiblePositionManager.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../NonfungiblePositionManager.sol'; + +contract MockTimeNonfungiblePositionManager is NonfungiblePositionManager { + uint256 time; + + constructor( + address _factory, + address _WETH9, + address _tokenDescriptor + ) NonfungiblePositionManager(_factory, _WETH9, _tokenDescriptor) {} + + function _blockTimestamp() internal view override returns (uint256) { + return time; + } + + function setTime(uint256 _time) external { + time = _time; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/MockTimeSwapRouter.sol b/lib/uniswap/v3-periphery/contracts/test/MockTimeSwapRouter.sol new file mode 100644 index 0000000..984707b --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/MockTimeSwapRouter.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../SwapRouter.sol'; + +contract MockTimeSwapRouter is SwapRouter { + uint256 time; + + constructor(address _factory, address _WETH9) SwapRouter(_factory, _WETH9) {} + + function _blockTimestamp() internal view override returns (uint256) { + return time; + } + + function setTime(uint256 _time) external { + time = _time; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/NFTDescriptorTest.sol b/lib/uniswap/v3-periphery/contracts/test/NFTDescriptorTest.sol new file mode 100644 index 0000000..89f783b --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/NFTDescriptorTest.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../libraries/NFTDescriptor.sol'; +import '../libraries/NFTSVG.sol'; +import '../libraries/HexStrings.sol'; + +contract NFTDescriptorTest { + using HexStrings for uint256; + + function constructTokenURI(NFTDescriptor.ConstructTokenURIParams calldata params) + public + pure + returns (string memory) + { + return NFTDescriptor.constructTokenURI(params); + } + + function getGasCostOfConstructTokenURI(NFTDescriptor.ConstructTokenURIParams calldata params) + public + view + returns (uint256) + { + uint256 gasBefore = gasleft(); + NFTDescriptor.constructTokenURI(params); + return gasBefore - gasleft(); + } + + function tickToDecimalString( + int24 tick, + int24 tickSpacing, + uint8 token0Decimals, + uint8 token1Decimals, + bool flipRatio + ) public pure returns (string memory) { + return NFTDescriptor.tickToDecimalString(tick, tickSpacing, token0Decimals, token1Decimals, flipRatio); + } + + function fixedPointToDecimalString( + uint160 sqrtRatioX96, + uint8 token0Decimals, + uint8 token1Decimals + ) public pure returns (string memory) { + return NFTDescriptor.fixedPointToDecimalString(sqrtRatioX96, token0Decimals, token1Decimals); + } + + function feeToPercentString(uint24 fee) public pure returns (string memory) { + return NFTDescriptor.feeToPercentString(fee); + } + + function addressToString(address _address) public pure returns (string memory) { + return NFTDescriptor.addressToString(_address); + } + + function generateSVGImage(NFTDescriptor.ConstructTokenURIParams memory params) public pure returns (string memory) { + return NFTDescriptor.generateSVGImage(params); + } + + function tokenToColorHex(address token, uint256 offset) public pure returns (string memory) { + return NFTDescriptor.tokenToColorHex(uint256(token), offset); + } + + function sliceTokenHex(address token, uint256 offset) public pure returns (uint256) { + return NFTDescriptor.sliceTokenHex(uint256(token), offset); + } + + function rangeLocation(int24 tickLower, int24 tickUpper) public pure returns (string memory, string memory) { + return NFTSVG.rangeLocation(tickLower, tickUpper); + } + + function isRare(uint256 tokenId, address poolAddress) public pure returns (bool) { + return NFTSVG.isRare(tokenId, poolAddress); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/NonfungiblePositionManagerPositionsGasTest.sol b/lib/uniswap/v3-periphery/contracts/test/NonfungiblePositionManagerPositionsGasTest.sol new file mode 100644 index 0000000..7bec623 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/NonfungiblePositionManagerPositionsGasTest.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/INonfungiblePositionManager.sol'; + +contract NonfungiblePositionManagerPositionsGasTest { + INonfungiblePositionManager immutable nonfungiblePositionManager; + + constructor(INonfungiblePositionManager _nonfungiblePositionManager) { + nonfungiblePositionManager = _nonfungiblePositionManager; + } + + function getGasCostOfPositions(uint256 tokenId) external view returns (uint256) { + uint256 gasBefore = gasleft(); + nonfungiblePositionManager.positions(tokenId); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/OracleTest.sol b/lib/uniswap/v3-periphery/contracts/test/OracleTest.sol new file mode 100644 index 0000000..b0cf921 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/OracleTest.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../libraries/OracleLibrary.sol'; + +contract OracleTest { + function consult(address pool, uint32 secondsAgo) + public + view + returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity) + { + return OracleLibrary.consult(pool, secondsAgo); + } + + function getQuoteAtTick( + int24 tick, + uint128 baseAmount, + address baseToken, + address quoteToken + ) public pure returns (uint256 quoteAmount) { + quoteAmount = OracleLibrary.getQuoteAtTick(tick, baseAmount, baseToken, quoteToken); + } + + // For gas snapshot test + function getGasCostOfConsult(address pool, uint32 period) public view returns (uint256) { + uint256 gasBefore = gasleft(); + OracleLibrary.consult(pool, period); + return gasBefore - gasleft(); + } + + function getGasCostOfGetQuoteAtTick( + int24 tick, + uint128 baseAmount, + address baseToken, + address quoteToken + ) public view returns (uint256) { + uint256 gasBefore = gasleft(); + OracleLibrary.getQuoteAtTick(tick, baseAmount, baseToken, quoteToken); + return gasBefore - gasleft(); + } + + function getOldestObservationSecondsAgo(address pool) + public + view + returns (uint32 secondsAgo, uint32 currentTimestamp) + { + secondsAgo = OracleLibrary.getOldestObservationSecondsAgo(pool); + currentTimestamp = uint32(block.timestamp); + } + + function getBlockStartingTickAndLiquidity(address pool) public view returns (int24, uint128) { + return OracleLibrary.getBlockStartingTickAndLiquidity(pool); + } + + function getWeightedArithmeticMeanTick(OracleLibrary.WeightedTickData[] memory observations) + public + pure + returns (int24 arithmeticMeanWeightedTick) + { + return OracleLibrary.getWeightedArithmeticMeanTick(observations); + } + + function getChainedPrice(address[] memory tokens, int24[] memory ticks) public view returns (int256 syntheticTick) { + return OracleLibrary.getChainedPrice(tokens, ticks); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/PathTest.sol b/lib/uniswap/v3-periphery/contracts/test/PathTest.sol new file mode 100644 index 0000000..1a7280e --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/PathTest.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/Path.sol'; + +contract PathTest { + function hasMultiplePools(bytes memory path) public pure returns (bool) { + return Path.hasMultiplePools(path); + } + + function decodeFirstPool(bytes memory path) + public + pure + returns ( + address tokenA, + address tokenB, + uint24 fee + ) + { + return Path.decodeFirstPool(path); + } + + function getFirstPool(bytes memory path) public pure returns (bytes memory) { + return Path.getFirstPool(path); + } + + function skipToken(bytes memory path) public pure returns (bytes memory) { + return Path.skipToken(path); + } + + // gas funcs + function getGasCostOfDecodeFirstPool(bytes memory path) public view returns (uint256) { + uint256 gasBefore = gasleft(); + Path.decodeFirstPool(path); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/PeripheryImmutableStateTest.sol b/lib/uniswap/v3-periphery/contracts/test/PeripheryImmutableStateTest.sol new file mode 100644 index 0000000..6298162 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/PeripheryImmutableStateTest.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity =0.7.6; + +import '../base/PeripheryImmutableState.sol'; + +contract PeripheryImmutableStateTest is PeripheryImmutableState { + constructor(address _factory, address _WETH9) PeripheryImmutableState(_factory, _WETH9) {} +} diff --git a/lib/uniswap/v3-periphery/contracts/test/PoolAddressTest.sol b/lib/uniswap/v3-periphery/contracts/test/PoolAddressTest.sol new file mode 100644 index 0000000..c1571e4 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/PoolAddressTest.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/PoolAddress.sol'; + +contract PoolAddressTest { + function POOL_INIT_CODE_HASH() external pure returns (bytes32) { + return PoolAddress.POOL_INIT_CODE_HASH; + } + + function computeAddress( + address factory, + address token0, + address token1, + uint24 fee + ) external pure returns (address) { + return PoolAddress.computeAddress(factory, PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee})); + } + + function getGasCostOfComputeAddress( + address factory, + address token0, + address token1, + uint24 fee + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + PoolAddress.computeAddress(factory, PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee})); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/PoolTicksCounterTest.sol b/lib/uniswap/v3-periphery/contracts/test/PoolTicksCounterTest.sol new file mode 100644 index 0000000..3d47bf7 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/PoolTicksCounterTest.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; + +pragma solidity >=0.6.0; + +import '../libraries/PoolTicksCounter.sol'; + +contract PoolTicksCounterTest { + using PoolTicksCounter for IUniswapV3Pool; + + function countInitializedTicksCrossed( + IUniswapV3Pool pool, + int24 tickBefore, + int24 tickAfter + ) external view returns (uint32 initializedTicksCrossed) { + return pool.countInitializedTicksCrossed(tickBefore, tickAfter); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/PositionValueTest.sol b/lib/uniswap/v3-periphery/contracts/test/PositionValueTest.sol new file mode 100644 index 0000000..7752f7a --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/PositionValueTest.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/PositionValue.sol'; +import '../interfaces/INonfungiblePositionManager.sol'; + +contract PositionValueTest { + function total( + INonfungiblePositionManager nft, + uint256 tokenId, + uint160 sqrtRatioX96 + ) external view returns (uint256 amount0, uint256 amount1) { + return PositionValue.total(nft, tokenId, sqrtRatioX96); + } + + function principal( + INonfungiblePositionManager nft, + uint256 tokenId, + uint160 sqrtRatioX96 + ) external view returns (uint256 amount0, uint256 amount1) { + return PositionValue.principal(nft, tokenId, sqrtRatioX96); + } + + function fees(INonfungiblePositionManager nft, uint256 tokenId) + external + view + returns (uint256 amount0, uint256 amount1) + { + return PositionValue.fees(nft, tokenId); + } + + function totalGas( + INonfungiblePositionManager nft, + uint256 tokenId, + uint160 sqrtRatioX96 + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + PositionValue.total(nft, tokenId, sqrtRatioX96); + return gasBefore - gasleft(); + } + + function principalGas( + INonfungiblePositionManager nft, + uint256 tokenId, + uint160 sqrtRatioX96 + ) external view returns (uint256) { + uint256 gasBefore = gasleft(); + PositionValue.principal(nft, tokenId, sqrtRatioX96); + return gasBefore - gasleft(); + } + + function feesGas(INonfungiblePositionManager nft, uint256 tokenId) external view returns (uint256) { + uint256 gasBefore = gasleft(); + PositionValue.fees(nft, tokenId); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/SelfPermitTest.sol b/lib/uniswap/v3-periphery/contracts/test/SelfPermitTest.sol new file mode 100644 index 0000000..1a3c022 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/SelfPermitTest.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../base/SelfPermit.sol'; + +/// @dev Same as SelfPermit but not abstract +contract SelfPermitTest is SelfPermit { + +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestCallbackValidation.sol b/lib/uniswap/v3-periphery/contracts/test/TestCallbackValidation.sol new file mode 100644 index 0000000..e0c2eeb --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestCallbackValidation.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../libraries/CallbackValidation.sol'; + +contract TestCallbackValidation { + function verifyCallback( + address factory, + address tokenA, + address tokenB, + uint24 fee + ) external view returns (IUniswapV3Pool pool) { + return CallbackValidation.verifyCallback(factory, tokenA, tokenB, fee); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestERC20.sol b/lib/uniswap/v3-periphery/contracts/test/TestERC20.sol new file mode 100644 index 0000000..66ed4ac --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestERC20.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '@openzeppelin/contracts/drafts/ERC20Permit.sol'; + +contract TestERC20 is ERC20Permit { + constructor(uint256 amountToMint) ERC20('Test ERC20', 'TEST') ERC20Permit('Test ERC20') { + _mint(msg.sender, amountToMint); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestERC20Metadata.sol b/lib/uniswap/v3-periphery/contracts/test/TestERC20Metadata.sol new file mode 100644 index 0000000..af94bf6 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestERC20Metadata.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '@openzeppelin/contracts/drafts/ERC20Permit.sol'; + +contract TestERC20Metadata is ERC20Permit { + constructor( + uint256 amountToMint, + string memory name, + string memory symbol + ) ERC20(name, symbol) ERC20Permit(name) { + _mint(msg.sender, amountToMint); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestERC20PermitAllowed.sol b/lib/uniswap/v3-periphery/contracts/test/TestERC20PermitAllowed.sol new file mode 100644 index 0000000..1bdf8ab --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestERC20PermitAllowed.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import './TestERC20.sol'; +import '../interfaces/external/IERC20PermitAllowed.sol'; + +// has a fake permit that just uses the other signature type for type(uint256).max +contract TestERC20PermitAllowed is TestERC20, IERC20PermitAllowed { + constructor(uint256 amountToMint) TestERC20(amountToMint) {} + + function permit( + address holder, + address spender, + uint256 nonce, + uint256 expiry, + bool allowed, + uint8 v, + bytes32 r, + bytes32 s + ) external override { + require(this.nonces(holder) == nonce, 'TestERC20PermitAllowed::permit: wrong nonce'); + permit(holder, spender, allowed ? type(uint256).max : 0, expiry, v, r, s); + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestMulticall.sol b/lib/uniswap/v3-periphery/contracts/test/TestMulticall.sol new file mode 100644 index 0000000..5d351d3 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestMulticall.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +pragma abicoder v2; + +import '../base/Multicall.sol'; + +contract TestMulticall is Multicall { + function functionThatRevertsWithError(string memory error) external pure { + revert(error); + } + + struct Tuple { + uint256 a; + uint256 b; + } + + function functionThatReturnsTuple(uint256 a, uint256 b) external pure returns (Tuple memory tuple) { + tuple = Tuple({b: a, a: b}); + } + + uint256 public paid; + + function pays() external payable { + paid += msg.value; + } + + function returnSender() external view returns (address) { + return msg.sender; + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestPositionNFTOwner.sol b/lib/uniswap/v3-periphery/contracts/test/TestPositionNFTOwner.sol new file mode 100644 index 0000000..58f1818 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestPositionNFTOwner.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '../interfaces/external/IERC1271.sol'; + +contract TestPositionNFTOwner is IERC1271 { + address public owner; + + function setOwner(address _owner) external { + owner = _owner; + } + + function isValidSignature(bytes32 hash, bytes memory signature) external view override returns (bytes4 magicValue) { + bytes32 r; + bytes32 s; + uint8 v; + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + if (ecrecover(hash, v, r, s) == owner) { + return bytes4(0x1626ba7e); + } else { + return bytes4(0); + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TestUniswapV3Callee.sol b/lib/uniswap/v3-periphery/contracts/test/TestUniswapV3Callee.sol new file mode 100644 index 0000000..1400bd6 --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TestUniswapV3Callee.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; + +import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol'; +import '@uniswap/v3-core/contracts/libraries/SafeCast.sol'; +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; + +contract TestUniswapV3Callee is IUniswapV3SwapCallback { + using SafeCast for uint256; + + function swapExact0For1( + address pool, + uint256 amount0In, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, true, amount0In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swap0ForExact1( + address pool, + uint256 amount1Out, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, true, -amount1Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swapExact1For0( + address pool, + uint256 amount1In, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, false, amount1In.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function swap1ForExact0( + address pool, + uint256 amount0Out, + address recipient, + uint160 sqrtPriceLimitX96 + ) external { + IUniswapV3Pool(pool).swap(recipient, false, -amount0Out.toInt256(), sqrtPriceLimitX96, abi.encode(msg.sender)); + } + + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external override { + address sender = abi.decode(data, (address)); + + if (amount0Delta > 0) { + IERC20(IUniswapV3Pool(msg.sender).token0()).transferFrom(sender, msg.sender, uint256(amount0Delta)); + } else { + assert(amount1Delta > 0); + IERC20(IUniswapV3Pool(msg.sender).token1()).transferFrom(sender, msg.sender, uint256(amount1Delta)); + } + } +} diff --git a/lib/uniswap/v3-periphery/contracts/test/TickLensTest.sol b/lib/uniswap/v3-periphery/contracts/test/TickLensTest.sol new file mode 100644 index 0000000..55cb7fd --- /dev/null +++ b/lib/uniswap/v3-periphery/contracts/test/TickLensTest.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; +pragma abicoder v2; + +import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import '../lens/TickLens.sol'; + +/// @title Tick Lens contract +contract TickLensTest is TickLens { + function getGasCostOfGetPopulatedTicksInWord(address pool, int16 tickBitmapIndex) external view returns (uint256) { + uint256 gasBefore = gasleft(); + getPopulatedTicksInWord(pool, tickBitmapIndex); + return gasBefore - gasleft(); + } +} diff --git a/lib/uniswap/v3-periphery/deploys.md b/lib/uniswap/v3-periphery/deploys.md new file mode 100644 index 0000000..c0af53c --- /dev/null +++ b/lib/uniswap/v3-periphery/deploys.md @@ -0,0 +1,25 @@ +# Deployment addresses + +The latest version of `@uniswap/v3-core`, `@uniswap/v3-periphery`, `@uniswap/swap-router-contracts`, and `@uniswap/v3-staker` are deployed at the addresses listed below. Integrators should **no longer assume that they are deployed to the same addresses across chains** and be extremely careful to confirm mappings below. + +| Contract | Mainnet, Polygon, Optimism, Arbitrum, Testnets Address | Celo Address | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | -------------------------------------------- | +| [UniswapV3Factory](https://github.com/Uniswap/uniswap-v3-core/blob/v1.0.0/contracts/UniswapV3Factory.sol) | `0x1F98431c8aD98523631AE4a59f267346ea31F984` | `0xAfE208a311B21f13EF87E33A90049fC17A7acDEc` | +| [Multicall2](https://etherscan.io/address/0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696#code) | `0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696` | `0x633987602DE5C4F337e3DbF265303A1080324204` | +| [ProxyAdmin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.1-solc-0.7-2/contracts/proxy/ProxyAdmin.sol) | `0xB753548F6E010e7e680BA186F9Ca1BdAB2E90cf2` | `0xc1b262Dd7643D4B7cA9e51631bBd900a564BF49A` | +| [TickLens](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/lens/TickLens.sol) | `0xbfd8137f7d1516D3ea5cA83523914859ec47F573` | `0x5f115D9113F88e0a0Db1b5033D90D4a9690AcD3D` | +| [Quoter](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/lens/Quoter.sol) | `0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6` | `0x82825d0554fA07f7FC52Ab63c961F330fdEFa8E8` | +| [SwapRouter](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/SwapRouter.sol) | `0xE592427A0AEce92De3Edee1F18E0157C05861564` | `0x5615CDAb10dc425a742d643d949a7F474C01abc4` | +| [NFTDescriptor](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/libraries/NFTDescriptor.sol) | `0x42B24A95702b9986e82d421cC3568932790A48Ec` | `0xa9Fd765d85938D278cb0b108DbE4BF7186831186` | +| [NonfungibleTokenPositionDescriptor](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/NonfungibleTokenPositionDescriptor.sol) | `0x91ae842A5Ffd8d12023116943e72A606179294f3` | `0x644023b316bB65175C347DE903B60a756F6dd554` | +| [TransparentUpgradeableProxy](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.1-solc-0.7-2/contracts/proxy/TransparentUpgradeableProxy.sol) | `0xEe6A57eC80ea46401049E92587E52f5Ec1c24785` | `0x505B43c452AA4443e0a6B84bb37771494633Fde9` | +| [NonfungiblePositionManager](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/NonfungiblePositionManager.sol) | `0xC36442b4a4522E871399CD717aBDD847Ab11FE88` | `0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A` | +| [V3Migrator](https://github.com/Uniswap/uniswap-v3-periphery/blob/v1.0.0/contracts/V3Migrator.sol) | `0xA5644E29708357803b5A882D272c41cC0dF92B34` | `0x3cFd4d48EDfDCC53D3f173F596f621064614C582` | + +These addresses are final and were deployed from these npm package versions: + +- `@uniswap/v3-core`: [`1.0.0`](https://github.com/Uniswap/uniswap-v3-core/tree/v1.0.0) +- `@uniswap/v3-periphery`: [`1.0.0`](https://github.com/Uniswap/uniswap-v3-periphery/tree/v1.0.0) + +The source code is verified with Etherscan on all networks, for all contracts except `UniswapV3Pool`. +We are working on getting the `UniswapV3Pool` contract verified with Etherscan. diff --git a/lib/uniswap/v3-periphery/hardhat.config.ts b/lib/uniswap/v3-periphery/hardhat.config.ts new file mode 100644 index 0000000..57343da --- /dev/null +++ b/lib/uniswap/v3-periphery/hardhat.config.ts @@ -0,0 +1,104 @@ +import '@nomiclabs/hardhat-ethers' +import '@nomiclabs/hardhat-etherscan' +import '@nomiclabs/hardhat-waffle' +import 'hardhat-typechain' +import 'hardhat-watcher' + +const LOW_OPTIMIZER_COMPILER_SETTINGS = { + version: '0.7.6', + settings: { + evmVersion: 'istanbul', + optimizer: { + enabled: true, + runs: 2_000, + }, + metadata: { + bytecodeHash: 'none', + }, + }, +} + +const LOWEST_OPTIMIZER_COMPILER_SETTINGS = { + version: '0.7.6', + settings: { + evmVersion: 'istanbul', + optimizer: { + enabled: true, + runs: 1_000, + }, + metadata: { + bytecodeHash: 'none', + }, + }, +} + +const DEFAULT_COMPILER_SETTINGS = { + version: '0.7.6', + settings: { + evmVersion: 'istanbul', + optimizer: { + enabled: true, + runs: 1_000_000, + }, + metadata: { + bytecodeHash: 'none', + }, + }, +} + +export default { + networks: { + hardhat: { + allowUnlimitedContractSize: false, + }, + mainnet: { + url: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + ropsten: { + url: `https://ropsten.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + rinkeby: { + url: `https://rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + goerli: { + url: `https://goerli.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + kovan: { + url: `https://kovan.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + arbitrumRinkeby: { + url: `https://arbitrum-rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + arbitrum: { + url: `https://arbitrum-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + optimismKovan: { + url: `https://optimism-kovan.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + optimism: { + url: `https://optimism-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`, + }, + }, + etherscan: { + // Your API key for Etherscan + // Obtain one at https://etherscan.io/ + apiKey: process.env.ETHERSCAN_API_KEY, + }, + solidity: { + compilers: [DEFAULT_COMPILER_SETTINGS], + overrides: { + 'contracts/NonfungiblePositionManager.sol': LOW_OPTIMIZER_COMPILER_SETTINGS, + 'contracts/test/MockTimeNonfungiblePositionManager.sol': LOW_OPTIMIZER_COMPILER_SETTINGS, + 'contracts/test/NFTDescriptorTest.sol': LOWEST_OPTIMIZER_COMPILER_SETTINGS, + 'contracts/NonfungibleTokenPositionDescriptor.sol': LOWEST_OPTIMIZER_COMPILER_SETTINGS, + 'contracts/libraries/NFTDescriptor.sol': LOWEST_OPTIMIZER_COMPILER_SETTINGS, + }, + }, + watcher: { + test: { + tasks: [{ command: 'test', params: { testFiles: ['{path}'] } }], + files: ['./test/**/*'], + verbose: true, + }, + }, +} diff --git a/lib/uniswap/v3-periphery/package.json b/lib/uniswap/v3-periphery/package.json new file mode 100644 index 0000000..a6bebf1 --- /dev/null +++ b/lib/uniswap/v3-periphery/package.json @@ -0,0 +1,68 @@ +{ + "name": "@uniswap/v3-periphery", + "description": "🎚 Peripheral smart contracts for interacting with Uniswap V3", + "license": "GPL-2.0-or-later", + "publishConfig": { + "access": "public" + }, + "version": "1.4.3", + "homepage": "https://uniswap.org", + "keywords": [ + "uniswap", + "periphery", + "v3" + ], + "repository": { + "type": "git", + "url": "https://github.com/Uniswap/uniswap-v3-periphery" + }, + "files": [ + "contracts/base", + "contracts/interfaces", + "contracts/libraries", + "artifacts/contracts/**/*.json", + "!artifacts/contracts/**/*.dbg.json", + "!artifacts/contracts/test/**/*", + "!artifacts/contracts/base/**/*" + ], + "engines": { + "node": ">=10" + }, + "dependencies": { + "@openzeppelin/contracts": "3.4.2-solc-0.7", + "@uniswap/lib": "^4.0.1-alpha", + "@uniswap/v2-core": "1.0.1", + "@uniswap/v3-core": "1.0.0", + "base64-sol": "1.0.1" + }, + "devDependencies": { + "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-etherscan": "^2.1.8", + "@nomiclabs/hardhat-waffle": "^2.0.1", + "@typechain/ethers-v5": "^4.0.0", + "@types/chai": "^4.2.6", + "@types/mocha": "^5.2.7", + "chai": "^4.2.0", + "decimal.js": "^10.2.1", + "ethereum-waffle": "^3.0.2", + "ethers": "^5.0.8", + "hardhat": "^2.6.8", + "hardhat-typechain": "^0.3.5", + "hardhat-watcher": "^2.1.1", + "is-svg": "^4.3.1", + "mocha": "^6.2.2", + "mocha-chai-jest-snapshot": "^1.1.0", + "prettier": "^2.0.5", + "prettier-plugin-solidity": "^1.0.0-beta.10", + "solhint": "^3.2.1", + "solhint-plugin-prettier": "^0.0.5", + "ts-generator": "^0.1.1", + "ts-node": "^8.5.4", + "typechain": "^4.0.0", + "typescript": "^3.7.3" + }, + "scripts": { + "compile": "hardhat compile", + "test": "hardhat test" + } +} diff --git a/lib/uniswap/v3-periphery/test/Base64.spec.ts b/lib/uniswap/v3-periphery/test/Base64.spec.ts new file mode 100644 index 0000000..5baef4b --- /dev/null +++ b/lib/uniswap/v3-periphery/test/Base64.spec.ts @@ -0,0 +1,81 @@ +import { ethers } from 'hardhat' +import { base64Encode } from './shared/base64' +import { expect } from './shared/expect' +import { Base64Test } from '../typechain' +import { randomBytes } from 'crypto' +import snapshotGasCost from './shared/snapshotGasCost' + +function stringToHex(str: string): string { + return `0x${Buffer.from(str, 'utf8').toString('hex')}` +} + +describe('Base64', () => { + let base64: Base64Test + before('deploy test contract', async () => { + base64 = (await (await ethers.getContractFactory('Base64Test')).deploy()) as Base64Test + }) + + describe('#encode', () => { + it('is correct for empty bytes', async () => { + expect(await base64.encode(stringToHex(''))).to.eq('') + }) + + for (const example of [ + 'test string', + 'this is a test', + 'alphabet soup', + 'aLpHaBeT', + 'includes\nnewlines', + '', + '😀', + 'f', + 'fo', + 'foo', + 'foob', + 'fooba', + 'foobar', + 'this is a very long string that should cost a lot of gas to encode :)', + ]) { + it(`works for "${example}"`, async () => { + expect(await base64.encode(stringToHex(example))).to.eq(base64Encode(example)) + }) + + it(`gas cost of encode(${example})`, async () => { + await snapshotGasCost(base64.getGasCostOfEncode(stringToHex(example))) + }) + } + + describe('max size string (24kB)', () => { + let str: string + before(() => { + str = Array(24 * 1024) + .fill(null) + .map((_, i) => String.fromCharCode(i % 1024)) + .join('') + }) + it('correctness', async () => { + expect(await base64.encode(stringToHex(str))).to.eq(base64Encode(str)) + }) + it('gas cost', async () => { + await snapshotGasCost(base64.getGasCostOfEncode(stringToHex(str))) + }) + }) + + it('tiny fuzzing', async () => { + const inputs = [] + for (let i = 0; i < 100; i++) { + inputs.push(randomBytes(Math.random() * 100)) + } + + const promises = inputs.map((input) => { + return base64.encode(`0x${input.toString('hex')}`) + }) + + const results = await Promise.all(promises) + + for (let i = 0; i < inputs.length; i++) { + expect(inputs[i].toString('base64')).to.eq(results[i]) + } + }).timeout(300_000) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/CallbackValidation.spec.ts b/lib/uniswap/v3-periphery/test/CallbackValidation.spec.ts new file mode 100644 index 0000000..98eea96 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/CallbackValidation.spec.ts @@ -0,0 +1,56 @@ +import { Contract, constants, Wallet } from 'ethers' +import { waffle, ethers } from 'hardhat' +import { Fixture } from 'ethereum-waffle' +import completeFixture from './shared/completeFixture' +import { expect } from './shared/expect' +import { TestERC20, TestCallbackValidation } from '../typechain' +import { FeeAmount } from './shared/constants' + +describe('CallbackValidation', () => { + let nonpairAddr: Wallet, wallets: Wallet[] + + const callbackValidationFixture: Fixture<{ + callbackValidation: TestCallbackValidation + tokens: [TestERC20, TestERC20] + factory: Contract + }> = async (wallets, provider) => { + const { factory } = await completeFixture(wallets, provider) + const tokenFactory = await ethers.getContractFactory('TestERC20') + const callbackValidationFactory = await ethers.getContractFactory('TestCallbackValidation') + const tokens: [TestERC20, TestERC20] = [ + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, // do not use maxu256 to avoid overflowing + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + ] + const callbackValidation = (await callbackValidationFactory.deploy()) as TestCallbackValidation + + return { + tokens, + callbackValidation, + factory, + } + } + + let callbackValidation: TestCallbackValidation + let tokens: [TestERC20, TestERC20] + let factory: Contract + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + ;[nonpairAddr, ...wallets] = await (ethers as any).getSigners() + + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ callbackValidation, tokens, factory } = await loadFixture(callbackValidationFixture)) + }) + + it('reverts when called from an address other than the associated UniswapV3Pool', async () => { + expect( + callbackValidation + .connect(nonpairAddr) + .verifyCallback(factory.address, tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + ).to.be.reverted + }) +}) diff --git a/lib/uniswap/v3-periphery/test/LiquidityAmounts.spec.ts b/lib/uniswap/v3-periphery/test/LiquidityAmounts.spec.ts new file mode 100644 index 0000000..b24dd3c --- /dev/null +++ b/lib/uniswap/v3-periphery/test/LiquidityAmounts.spec.ts @@ -0,0 +1,241 @@ +import { ethers } from 'hardhat' +import { LiquidityAmountsTest } from '../typechain/LiquidityAmountsTest' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expect } from './shared/expect' + +import snapshotGasCost from './shared/snapshotGasCost' + +describe('LiquidityAmounts', async () => { + let liquidityFromAmounts: LiquidityAmountsTest + + before('deploy test library', async () => { + const liquidityFromAmountsTestFactory = await ethers.getContractFactory('LiquidityAmountsTest') + liquidityFromAmounts = (await liquidityFromAmountsTestFactory.deploy()) as LiquidityAmountsTest + }) + + describe('#getLiquidityForAmount0', () => { + it('gas', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost(liquidityFromAmounts.getGasCostOfGetLiquidityForAmount0(sqrtPriceAX96, sqrtPriceBX96, 100)) + }) + }) + + describe('#getLiquidityForAmount1', () => { + it('gas', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost(liquidityFromAmounts.getGasCostOfGetLiquidityForAmount1(sqrtPriceAX96, sqrtPriceBX96, 100)) + }) + }) + + describe('#getLiquidityForAmounts', () => { + it('amounts for price inside', async () => { + const sqrtPriceX96 = encodePriceSqrt(1, 1) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const liquidity = await liquidityFromAmounts.getLiquidityForAmounts( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 100, + 200 + ) + expect(liquidity).to.eq(2148) + }) + + it('amounts for price below', async () => { + const sqrtPriceX96 = encodePriceSqrt(99, 110) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const liquidity = await liquidityFromAmounts.getLiquidityForAmounts( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 100, + 200 + ) + expect(liquidity).to.eq(1048) + }) + + it('amounts for price above', async () => { + const sqrtPriceX96 = encodePriceSqrt(111, 100) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const liquidity = await liquidityFromAmounts.getLiquidityForAmounts( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 100, + 200 + ) + expect(liquidity).to.eq(2097) + }) + + it('amounts for price equal to lower boundary', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceX96 = sqrtPriceAX96 + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const liquidity = await liquidityFromAmounts.getLiquidityForAmounts( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 100, + 200 + ) + expect(liquidity).to.eq(1048) + }) + + it('amounts for price equal to upper boundary', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const sqrtPriceX96 = sqrtPriceBX96 + const liquidity = await liquidityFromAmounts.getLiquidityForAmounts( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 100, + 200 + ) + expect(liquidity).to.eq(2097) + }) + + it('gas for price below', async () => { + const sqrtPriceX96 = encodePriceSqrt(99, 110) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost( + liquidityFromAmounts.getGasCostOfGetLiquidityForAmounts(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, 100, 200) + ) + }) + it('gas for price above', async () => { + const sqrtPriceX96 = encodePriceSqrt(111, 100) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost( + liquidityFromAmounts.getGasCostOfGetLiquidityForAmounts(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, 100, 200) + ) + }) + it('gas for price inside', async () => { + const sqrtPriceX96 = encodePriceSqrt(1, 1) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost( + liquidityFromAmounts.getGasCostOfGetLiquidityForAmounts(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, 100, 200) + ) + }) + }) + + describe('#getAmount0ForLiquidity', () => { + it('gas', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost(liquidityFromAmounts.getGasCostOfGetAmount0ForLiquidity(sqrtPriceAX96, sqrtPriceBX96, 100)) + }) + }) + + describe('#getLiquidityForAmount1', () => { + it('gas', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost(liquidityFromAmounts.getGasCostOfGetAmount1ForLiquidity(sqrtPriceAX96, sqrtPriceBX96, 100)) + }) + }) + + describe('#getAmountsForLiquidity', () => { + it('amounts for price inside', async () => { + const sqrtPriceX96 = encodePriceSqrt(1, 1) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const { amount0, amount1 } = await liquidityFromAmounts.getAmountsForLiquidity( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 2148 + ) + expect(amount0).to.eq(99) + expect(amount1).to.eq(99) + }) + + it('amounts for price below', async () => { + const sqrtPriceX96 = encodePriceSqrt(99, 110) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const { amount0, amount1 } = await liquidityFromAmounts.getAmountsForLiquidity( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 1048 + ) + expect(amount0).to.eq(99) + expect(amount1).to.eq(0) + }) + + it('amounts for price above', async () => { + const sqrtPriceX96 = encodePriceSqrt(111, 100) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const { amount0, amount1 } = await liquidityFromAmounts.getAmountsForLiquidity( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 2097 + ) + expect(amount0).to.eq(0) + expect(amount1).to.eq(199) + }) + + it('amounts for price on lower boundary', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceX96 = sqrtPriceAX96 + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const { amount0, amount1 } = await liquidityFromAmounts.getAmountsForLiquidity( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 1048 + ) + expect(amount0).to.eq(99) + expect(amount1).to.eq(0) + }) + + it('amounts for price on upper boundary', async () => { + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + const sqrtPriceX96 = sqrtPriceBX96 + const { amount0, amount1 } = await liquidityFromAmounts.getAmountsForLiquidity( + sqrtPriceX96, + sqrtPriceAX96, + sqrtPriceBX96, + 2097 + ) + expect(amount0).to.eq(0) + expect(amount1).to.eq(199) + }) + + it('gas for price below', async () => { + const sqrtPriceX96 = encodePriceSqrt(99, 110) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost( + liquidityFromAmounts.getGasCostOfGetAmountsForLiquidity(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, 2148) + ) + }) + it('gas for price above', async () => { + const sqrtPriceX96 = encodePriceSqrt(111, 100) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost( + liquidityFromAmounts.getGasCostOfGetAmountsForLiquidity(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, 1048) + ) + }) + it('gas for price inside', async () => { + const sqrtPriceX96 = encodePriceSqrt(1, 1) + const sqrtPriceAX96 = encodePriceSqrt(100, 110) + const sqrtPriceBX96 = encodePriceSqrt(110, 100) + await snapshotGasCost( + liquidityFromAmounts.getGasCostOfGetAmountsForLiquidity(sqrtPriceX96, sqrtPriceAX96, sqrtPriceBX96, 2097) + ) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/Multicall.spec.ts b/lib/uniswap/v3-periphery/test/Multicall.spec.ts new file mode 100644 index 0000000..44ca334 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/Multicall.spec.ts @@ -0,0 +1,65 @@ +import { Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { TestMulticall } from '../typechain/TestMulticall' +import { expect } from './shared/expect' + +import snapshotGasCost from './shared/snapshotGasCost' + +describe('Multicall', async () => { + let wallets: Wallet[] + + let multicall: TestMulticall + + before('get wallets', async () => { + wallets = await (ethers as any).getSigners() + }) + + beforeEach('create multicall', async () => { + const multicallTestFactory = await ethers.getContractFactory('TestMulticall') + multicall = (await multicallTestFactory.deploy()) as TestMulticall + }) + + it('revert messages are returned', async () => { + await expect( + multicall.multicall([multicall.interface.encodeFunctionData('functionThatRevertsWithError', ['abcdef'])]) + ).to.be.revertedWith('abcdef') + }) + + it('return data is properly encoded', async () => { + const [data] = await multicall.callStatic.multicall([ + multicall.interface.encodeFunctionData('functionThatReturnsTuple', ['1', '2']), + ]) + const { + tuple: { a, b }, + } = multicall.interface.decodeFunctionResult('functionThatReturnsTuple', data) + expect(b).to.eq(1) + expect(a).to.eq(2) + }) + + describe('context is preserved', () => { + it('msg.value', async () => { + await multicall.multicall([multicall.interface.encodeFunctionData('pays')], { value: 3 }) + expect(await multicall.paid()).to.eq(3) + }) + + it('msg.value used twice', async () => { + await multicall.multicall( + [multicall.interface.encodeFunctionData('pays'), multicall.interface.encodeFunctionData('pays')], + { value: 3 } + ) + expect(await multicall.paid()).to.eq(6) + }) + + it('msg.sender', async () => { + expect(await multicall.returnSender()).to.eq(wallets[0].address) + }) + }) + + it('gas cost of pay w/o multicall', async () => { + await snapshotGasCost(multicall.pays({ value: 3 })) + }) + + it('gas cost of pay w/ multicall', async () => { + await snapshotGasCost(multicall.multicall([multicall.interface.encodeFunctionData('pays')], { value: 3 })) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/NFTDescriptor.spec.ts b/lib/uniswap/v3-periphery/test/NFTDescriptor.spec.ts new file mode 100644 index 0000000..fce07b1 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/NFTDescriptor.spec.ts @@ -0,0 +1,907 @@ +import { BigNumber, constants, Wallet } from 'ethers' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { waffle, ethers } from 'hardhat' +import { expect } from './shared/expect' +import { TestERC20Metadata, NFTDescriptorTest } from '../typechain' +import { Fixture } from 'ethereum-waffle' +import { FeeAmount, TICK_SPACINGS } from './shared/constants' +import snapshotGasCost from './shared/snapshotGasCost' +import { formatSqrtRatioX96 } from './shared/formatSqrtRatioX96' +import { getMaxTick, getMinTick } from './shared/ticks' +import { randomBytes } from 'crypto' +import { extractJSONFromURI } from './shared/extractJSONFromURI' +import fs from 'fs' +import isSvg from 'is-svg' + +const TEN = BigNumber.from(10) +const LOWEST_SQRT_RATIO = 4310618292 +const HIGHEST_SQRT_RATIO = BigNumber.from(33849).mul(TEN.pow(34)) + +describe('NFTDescriptor', () => { + let wallets: Wallet[] + + const nftDescriptorFixture: Fixture<{ + tokens: [TestERC20Metadata, TestERC20Metadata, TestERC20Metadata, TestERC20Metadata] + nftDescriptor: NFTDescriptorTest + }> = async (wallets, provider) => { + const nftDescriptorLibraryFactory = await ethers.getContractFactory('NFTDescriptor') + const nftDescriptorLibrary = await nftDescriptorLibraryFactory.deploy() + + const tokenFactory = await ethers.getContractFactory('TestERC20Metadata') + const NFTDescriptorFactory = await ethers.getContractFactory('NFTDescriptorTest', { + libraries: { + NFTDescriptor: nftDescriptorLibrary.address, + }, + }) + const nftDescriptor = (await NFTDescriptorFactory.deploy()) as NFTDescriptorTest + const TestERC20Metadata = tokenFactory.deploy(constants.MaxUint256.div(2), 'Test ERC20', 'TEST1') + const tokens: [TestERC20Metadata, TestERC20Metadata, TestERC20Metadata, TestERC20Metadata] = [ + (await tokenFactory.deploy(constants.MaxUint256.div(2), 'Test ERC20', 'TEST1')) as TestERC20Metadata, // do not use maxu256 to avoid overflowing + (await tokenFactory.deploy(constants.MaxUint256.div(2), 'Test ERC20', 'TEST2')) as TestERC20Metadata, + (await tokenFactory.deploy(constants.MaxUint256.div(2), 'Test ERC20', 'TEST3')) as TestERC20Metadata, + (await tokenFactory.deploy(constants.MaxUint256.div(2), 'Test ERC20', 'TEST4')) as TestERC20Metadata, + ] + tokens.sort((a, b) => (a.address.toLowerCase() < b.address.toLowerCase() ? -1 : 1)) + return { + nftDescriptor, + tokens, + } + } + + let nftDescriptor: NFTDescriptorTest + let tokens: [TestERC20Metadata, TestERC20Metadata, TestERC20Metadata, TestERC20Metadata] + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + wallets = await (ethers as any).getSigners() + + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ nftDescriptor, tokens } = await loadFixture(nftDescriptorFixture)) + }) + + describe('#constructTokenURI', () => { + let tokenId: number + let baseTokenAddress: string + let quoteTokenAddress: string + let baseTokenSymbol: string + let quoteTokenSymbol: string + let baseTokenDecimals: number + let quoteTokenDecimals: number + let flipRatio: boolean + let tickLower: number + let tickUpper: number + let tickCurrent: number + let tickSpacing: number + let fee: number + let poolAddress: string + + beforeEach(async () => { + tokenId = 123 + baseTokenAddress = tokens[0].address + quoteTokenAddress = tokens[1].address + baseTokenSymbol = await tokens[0].symbol() + quoteTokenSymbol = await tokens[1].symbol() + baseTokenDecimals = await tokens[0].decimals() + quoteTokenDecimals = await tokens[1].decimals() + flipRatio = false + tickLower = getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]) + tickUpper = getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]) + tickCurrent = 0 + tickSpacing = TICK_SPACINGS[FeeAmount.MEDIUM] + fee = 3000 + poolAddress = `0x${'b'.repeat(40)}` + }) + + it('returns the valid JSON string with min and max ticks', async () => { + const json = extractJSONFromURI( + await nftDescriptor.constructTokenURI({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ) + + const tokenUri = constructTokenMetadata( + tokenId, + quoteTokenAddress, + baseTokenAddress, + poolAddress, + quoteTokenSymbol, + baseTokenSymbol, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + '0.3%', + 'MIN<>MAX' + ) + + expect(json.description).to.equal(tokenUri.description) + expect(json.name).to.equal(tokenUri.name) + }) + + it('returns the valid JSON string with mid ticks', async () => { + tickLower = -10 + tickUpper = 10 + tickSpacing = TICK_SPACINGS[FeeAmount.MEDIUM] + fee = 3000 + + const json = extractJSONFromURI( + await nftDescriptor.constructTokenURI({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ) + + const tokenMetadata = constructTokenMetadata( + tokenId, + quoteTokenAddress, + baseTokenAddress, + poolAddress, + quoteTokenSymbol, + baseTokenSymbol, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + '0.3%', + '0.99900<>1.0010' + ) + + expect(json.description).to.equal(tokenMetadata.description) + expect(json.name).to.equal(tokenMetadata.name) + }) + + it('returns valid JSON when token symbols contain quotes', async () => { + quoteTokenSymbol = '"TES"T1"' + const json = extractJSONFromURI( + await nftDescriptor.constructTokenURI({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ) + + const tokenMetadata = constructTokenMetadata( + tokenId, + quoteTokenAddress, + baseTokenAddress, + poolAddress, + quoteTokenSymbol, + baseTokenSymbol, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + '0.3%', + 'MIN<>MAX' + ) + + expect(json.description).to.equal(tokenMetadata.description) + expect(json.name).to.equal(tokenMetadata.name) + }) + + describe('when the token ratio is flipped', () => { + it('returns the valid JSON for mid ticks', async () => { + flipRatio = true + tickLower = -10 + tickUpper = 10 + + const json = extractJSONFromURI( + await nftDescriptor.constructTokenURI({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ) + + const tokenMetadata = constructTokenMetadata( + tokenId, + quoteTokenAddress, + baseTokenAddress, + poolAddress, + quoteTokenSymbol, + baseTokenSymbol, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + '0.3%', + '0.99900<>1.0010' + ) + + expect(json.description).to.equal(tokenMetadata.description) + expect(json.name).to.equal(tokenMetadata.name) + }) + + it('returns the valid JSON for min/max ticks', async () => { + flipRatio = true + + const json = extractJSONFromURI( + await nftDescriptor.constructTokenURI({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ) + + const tokenMetadata = constructTokenMetadata( + tokenId, + quoteTokenAddress, + baseTokenAddress, + poolAddress, + quoteTokenSymbol, + baseTokenSymbol, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + '0.3%', + 'MIN<>MAX' + ) + + expect(json.description).to.equal(tokenMetadata.description) + expect(json.name).to.equal(tokenMetadata.name) + }) + }) + + it('gas', async () => { + await snapshotGasCost( + nftDescriptor.getGasCostOfConstructTokenURI({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ) + }) + + it('snapshot matches', async () => { + // get snapshot with super rare special sparkle + tokenId = 1 + poolAddress = `0x${'b'.repeat(40)}` + // get a snapshot with svg fade + tickCurrent = -1 + tickLower = 0 + tickUpper = 1000 + tickSpacing = TICK_SPACINGS[FeeAmount.LOW] + fee = FeeAmount.LOW + quoteTokenAddress = '0xabcdeabcdefabcdefabcdefabcdefabcdefabcdf' + baseTokenAddress = '0x1234567890123456789123456789012345678901' + quoteTokenSymbol = 'UNI' + baseTokenSymbol = 'WETH' + expect( + await nftDescriptor.constructTokenURI({ + tokenId, + quoteTokenAddress, + baseTokenAddress, + quoteTokenSymbol, + baseTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + ).toMatchSnapshot() + }) + }) + + describe('#addressToString', () => { + it('returns the correct string for a given address', async () => { + let addressStr = await nftDescriptor.addressToString(`0x${'1234abcdef'.repeat(4)}`) + expect(addressStr).to.eq('0x1234abcdef1234abcdef1234abcdef1234abcdef') + addressStr = await nftDescriptor.addressToString(`0x${'1'.repeat(40)}`) + expect(addressStr).to.eq(`0x${'1'.repeat(40)}`) + }) + }) + + describe('#tickToDecimalString', () => { + let tickSpacing: number + let minTick: number + let maxTick: number + + describe('when tickspacing is 10', () => { + before(() => { + tickSpacing = TICK_SPACINGS[FeeAmount.LOW] + minTick = getMinTick(tickSpacing) + maxTick = getMaxTick(tickSpacing) + }) + + it('returns MIN on lowest tick', async () => { + expect(await nftDescriptor.tickToDecimalString(minTick, tickSpacing, 18, 18, false)).to.equal('MIN') + }) + + it('returns MAX on the highest tick', async () => { + expect(await nftDescriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false)).to.equal('MAX') + }) + + it('returns the correct decimal string when the tick is in range', async () => { + expect(await nftDescriptor.tickToDecimalString(1, tickSpacing, 18, 18, false)).to.equal('1.0001') + }) + + it('returns the correct decimal string when tick is mintick for different tickspace', async () => { + const otherMinTick = getMinTick(TICK_SPACINGS[FeeAmount.HIGH]) + expect(await nftDescriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false)).to.equal( + '0.0000000000000000000000000000000000000029387' + ) + }) + }) + + describe('when tickspacing is 60', () => { + before(() => { + tickSpacing = TICK_SPACINGS[FeeAmount.MEDIUM] + minTick = getMinTick(tickSpacing) + maxTick = getMaxTick(tickSpacing) + }) + + it('returns MIN on lowest tick', async () => { + expect(await nftDescriptor.tickToDecimalString(minTick, tickSpacing, 18, 18, false)).to.equal('MIN') + }) + + it('returns MAX on the highest tick', async () => { + expect(await nftDescriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false)).to.equal('MAX') + }) + + it('returns the correct decimal string when the tick is in range', async () => { + expect(await nftDescriptor.tickToDecimalString(-1, tickSpacing, 18, 18, false)).to.equal('0.99990') + }) + + it('returns the correct decimal string when tick is mintick for different tickspace', async () => { + const otherMinTick = getMinTick(TICK_SPACINGS[FeeAmount.HIGH]) + expect(await nftDescriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false)).to.equal( + '0.0000000000000000000000000000000000000029387' + ) + }) + }) + + describe('when tickspacing is 200', () => { + before(() => { + tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + minTick = getMinTick(tickSpacing) + maxTick = getMaxTick(tickSpacing) + }) + + it('returns MIN on lowest tick', async () => { + expect(await nftDescriptor.tickToDecimalString(minTick, tickSpacing, 18, 18, false)).to.equal('MIN') + }) + + it('returns MAX on the highest tick', async () => { + expect(await nftDescriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false)).to.equal('MAX') + }) + + it('returns the correct decimal string when the tick is in range', async () => { + expect(await nftDescriptor.tickToDecimalString(0, tickSpacing, 18, 18, false)).to.equal('1.0000') + }) + + it('returns the correct decimal string when tick is mintick for different tickspace', async () => { + const otherMinTick = getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]) + expect(await nftDescriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false)).to.equal( + '0.0000000000000000000000000000000000000029387' + ) + }) + }) + + describe('when token ratio is flipped', () => { + it('returns the inverse of default ratio for medium sized numbers', async () => { + const tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + expect(await nftDescriptor.tickToDecimalString(10, tickSpacing, 18, 18, false)).to.eq('1.0010') + expect(await nftDescriptor.tickToDecimalString(10, tickSpacing, 18, 18, true)).to.eq('0.99900') + }) + + it('returns the inverse of default ratio for large numbers', async () => { + const tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + expect(await nftDescriptor.tickToDecimalString(487272, tickSpacing, 18, 18, false)).to.eq( + '1448400000000000000000' + ) + expect(await nftDescriptor.tickToDecimalString(487272, tickSpacing, 18, 18, true)).to.eq( + '0.00000000000000000000069041' + ) + }) + + it('returns the inverse of default ratio for small numbers', async () => { + const tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + expect(await nftDescriptor.tickToDecimalString(-387272, tickSpacing, 18, 18, false)).to.eq( + '0.000000000000000015200' + ) + expect(await nftDescriptor.tickToDecimalString(-387272, tickSpacing, 18, 18, true)).to.eq('65791000000000000') + }) + + it('returns the correct string with differing token decimals', async () => { + const tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + expect(await nftDescriptor.tickToDecimalString(1000, tickSpacing, 18, 18, true)).to.eq('0.90484') + expect(await nftDescriptor.tickToDecimalString(1000, tickSpacing, 18, 10, true)).to.eq('90484000') + expect(await nftDescriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true)).to.eq('0.0000000090484') + }) + + it('returns MIN for highest tick', async () => { + const tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + const lowestTick = getMinTick(TICK_SPACINGS[FeeAmount.HIGH]) + expect(await nftDescriptor.tickToDecimalString(lowestTick, tickSpacing, 18, 18, true)).to.eq('MAX') + }) + + it('returns MAX for lowest tick', async () => { + const tickSpacing = TICK_SPACINGS[FeeAmount.HIGH] + const highestTick = getMaxTick(TICK_SPACINGS[FeeAmount.HIGH]) + expect(await nftDescriptor.tickToDecimalString(highestTick, tickSpacing, 18, 18, true)).to.eq('MIN') + }) + }) + }) + + describe('#fixedPointToDecimalString', () => { + describe('returns the correct string for', () => { + it('the highest possible price', async () => { + const ratio = encodePriceSqrt(33849, 1 / 10 ** 34) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq( + '338490000000000000000000000000000000000' + ) + }) + + it('large numbers', async () => { + let ratio = encodePriceSqrt(25811, 1 / 10 ** 11) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('2581100000000000') + ratio = encodePriceSqrt(17662, 1 / 10 ** 5) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('1766200000') + }) + + it('exactly 5 sigfig whole number', async () => { + const ratio = encodePriceSqrt(42026, 1) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('42026') + }) + + it('when the decimal is at index 4', async () => { + const ratio = encodePriceSqrt(12087, 10) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('1208.7') + }) + + it('when the decimal is at index 3', async () => { + const ratio = encodePriceSqrt(12087, 100) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('120.87') + }) + + it('when the decimal is at index 2', async () => { + const ratio = encodePriceSqrt(12087, 1000) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('12.087') + }) + + it('when the decimal is at index 1', async () => { + const ratio = encodePriceSqrt(12345, 10000) + const bla = await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('1.2345') + }) + + it('when sigfigs have trailing 0s after the decimal', async () => { + const ratio = encodePriceSqrt(1, 1) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('1.0000') + }) + + it('when there are exactly 5 numbers after the decimal', async () => { + const ratio = encodePriceSqrt(12345, 100000) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('0.12345') + }) + + it('very small numbers', async () => { + let ratio = encodePriceSqrt(38741, 10 ** 20) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq('0.00000000000000038741') + ratio = encodePriceSqrt(88498, 10 ** 35) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq( + '0.00000000000000000000000000000088498' + ) + }) + + it('smallest number', async () => { + const ratio = encodePriceSqrt(39000, 10 ** 43) + expect(await nftDescriptor.fixedPointToDecimalString(ratio, 18, 18)).to.eq( + '0.0000000000000000000000000000000000000029387' + ) + }) + }) + + describe('when tokens have different decimal precision', () => { + describe('when baseToken has more precision decimals than quoteToken', () => { + it('returns the correct string when the decimal difference is even', async () => { + expect(await nftDescriptor.fixedPointToDecimalString(encodePriceSqrt(1, 1), 18, 16)).to.eq('100.00') + }) + + it('returns the correct string when the decimal difference is odd', async () => { + const tenRatio = encodePriceSqrt(10, 1) + expect(await nftDescriptor.fixedPointToDecimalString(tenRatio, 18, 17)).to.eq('100.00') + }) + + it('does not account for higher token0 precision if difference is more than 18', async () => { + expect(await nftDescriptor.fixedPointToDecimalString(encodePriceSqrt(1, 1), 24, 5)).to.eq('1.0000') + }) + }) + + describe('when quoteToken has more precision decimals than baseToken', () => { + it('returns the correct string when the decimal difference is even', async () => { + expect(await nftDescriptor.fixedPointToDecimalString(encodePriceSqrt(1, 1), 10, 18)).to.eq('0.000000010000') + }) + + it('returns the correct string when the decimal difference is odd', async () => { + expect(await nftDescriptor.fixedPointToDecimalString(encodePriceSqrt(1, 1), 7, 18)).to.eq('0.000000000010000') + }) + + // TODO: provide compatibility token prices that breach minimum price due to token decimal differences + it.skip('returns the correct string when the decimal difference brings ratio below the minimum', async () => { + const lowRatio = encodePriceSqrt(88498, 10 ** 35) + expect(await nftDescriptor.fixedPointToDecimalString(lowRatio, 10, 20)).to.eq( + '0.000000000000000000000000000000000000000088498' + ) + }) + + it('does not account for higher token1 precision if difference is more than 18', async () => { + expect(await nftDescriptor.fixedPointToDecimalString(encodePriceSqrt(1, 1), 24, 5)).to.eq('1.0000') + }) + }) + + it('some fuzz', async () => { + const random = (min: number, max: number): number => { + return Math.floor(min + ((Math.random() * 100) % (max + 1 - min))) + } + + const inputs: [BigNumber, number, number][] = [] + let i = 0 + while (i <= 20) { + const ratio = BigNumber.from(`0x${randomBytes(random(7, 20)).toString('hex')}`) + const decimals0 = random(3, 21) + const decimals1 = random(3, 21) + const decimalDiff = Math.abs(decimals0 - decimals1) + + // TODO: Address edgecase out of bounds prices due to decimal differences + if ( + ratio.div(TEN.pow(decimalDiff)).gt(LOWEST_SQRT_RATIO) && + ratio.mul(TEN.pow(decimalDiff)).lt(HIGHEST_SQRT_RATIO) + ) { + inputs.push([ratio, decimals0, decimals1]) + i++ + } + } + + for (let i in inputs) { + let ratio: BigNumber | number + let decimals0: number + let decimals1: number + ;[ratio, decimals0, decimals1] = inputs[i] + let result = await nftDescriptor.fixedPointToDecimalString(ratio, decimals0, decimals1) + expect(formatSqrtRatioX96(ratio, decimals0, decimals1)).to.eq(result) + } + }).timeout(300_000) + }) + }) + + describe('#feeToPercentString', () => { + it('returns the correct fee for 0', async () => { + expect(await nftDescriptor.feeToPercentString(0)).to.eq('0%') + }) + + it('returns the correct fee for 1', async () => { + expect(await nftDescriptor.feeToPercentString(1)).to.eq('0.0001%') + }) + + it('returns the correct fee for 30', async () => { + expect(await nftDescriptor.feeToPercentString(30)).to.eq('0.003%') + }) + + it('returns the correct fee for 33', async () => { + expect(await nftDescriptor.feeToPercentString(33)).to.eq('0.0033%') + }) + + it('returns the correct fee for 500', async () => { + expect(await nftDescriptor.feeToPercentString(500)).to.eq('0.05%') + }) + + it('returns the correct fee for 2500', async () => { + expect(await nftDescriptor.feeToPercentString(2500)).to.eq('0.25%') + }) + + it('returns the correct fee for 3000', async () => { + expect(await nftDescriptor.feeToPercentString(3000)).to.eq('0.3%') + }) + + it('returns the correct fee for 10000', async () => { + expect(await nftDescriptor.feeToPercentString(10000)).to.eq('1%') + }) + + it('returns the correct fee for 17000', async () => { + expect(await nftDescriptor.feeToPercentString(17000)).to.eq('1.7%') + }) + + it('returns the correct fee for 100000', async () => { + expect(await nftDescriptor.feeToPercentString(100000)).to.eq('10%') + }) + + it('returns the correct fee for 150000', async () => { + expect(await nftDescriptor.feeToPercentString(150000)).to.eq('15%') + }) + + it('returns the correct fee for 102000', async () => { + expect(await nftDescriptor.feeToPercentString(102000)).to.eq('10.2%') + }) + + it('returns the correct fee for 10000000', async () => { + expect(await nftDescriptor.feeToPercentString(1000000)).to.eq('100%') + }) + + it('returns the correct fee for 1005000', async () => { + expect(await nftDescriptor.feeToPercentString(1005000)).to.eq('100.5%') + }) + + it('returns the correct fee for 10000000', async () => { + expect(await nftDescriptor.feeToPercentString(10000000)).to.eq('1000%') + }) + + it('returns the correct fee for 12300000', async () => { + expect(await nftDescriptor.feeToPercentString(12300000)).to.eq('1230%') + }) + }) + + describe('#tokenToColorHex', () => { + function tokenToColorHex(tokenAddress: string, startIndex: number): string { + return `${tokenAddress.slice(startIndex, startIndex + 6).toLowerCase()}` + } + + it('returns the correct hash for the first 3 bytes of the token address', async () => { + expect(await nftDescriptor.tokenToColorHex(tokens[0].address, 136)).to.eq(tokenToColorHex(tokens[0].address, 2)) + expect(await nftDescriptor.tokenToColorHex(tokens[1].address, 136)).to.eq(tokenToColorHex(tokens[1].address, 2)) + }) + + it('returns the correct hash for the last 3 bytes of the address', async () => { + expect(await nftDescriptor.tokenToColorHex(tokens[0].address, 0)).to.eq(tokenToColorHex(tokens[0].address, 36)) + expect(await nftDescriptor.tokenToColorHex(tokens[1].address, 0)).to.eq(tokenToColorHex(tokens[1].address, 36)) + }) + }) + + describe('#rangeLocation', () => { + it('returns the correct coordinates when range midpoint under -125_000', async () => { + const coords = await nftDescriptor.rangeLocation(-887_272, -887_100) + expect(coords[0]).to.eq('8') + expect(coords[1]).to.eq('7') + }) + + it('returns the correct coordinates when range midpoint is between -125_000 and -75_000', async () => { + const coords = await nftDescriptor.rangeLocation(-100_000, -90_000) + expect(coords[0]).to.eq('8') + expect(coords[1]).to.eq('10.5') + }) + + it('returns the correct coordinates when range midpoint is between -75_000 and -25_000', async () => { + const coords = await nftDescriptor.rangeLocation(-50_000, -20_000) + expect(coords[0]).to.eq('8') + expect(coords[1]).to.eq('14.25') + }) + + it('returns the correct coordinates when range midpoint is between -25_000 and -5_000', async () => { + const coords = await nftDescriptor.rangeLocation(-10_000, -5_000) + expect(coords[0]).to.eq('10') + expect(coords[1]).to.eq('18') + }) + + it('returns the correct coordinates when range midpoint is between -5_000 and 0', async () => { + const coords = await nftDescriptor.rangeLocation(-5_000, -4_000) + expect(coords[0]).to.eq('11') + expect(coords[1]).to.eq('21') + }) + + it('returns the correct coordinates when range midpoint is between 0 and 5_000', async () => { + const coords = await nftDescriptor.rangeLocation(4_000, 5_000) + expect(coords[0]).to.eq('13') + expect(coords[1]).to.eq('23') + }) + + it('returns the correct coordinates when range midpoint is between 5_000 and 25_000', async () => { + const coords = await nftDescriptor.rangeLocation(10_000, 15_000) + expect(coords[0]).to.eq('15') + expect(coords[1]).to.eq('25') + }) + + it('returns the correct coordinates when range midpoint is between 25_000 and 75_000', async () => { + const coords = await nftDescriptor.rangeLocation(25_000, 50_000) + expect(coords[0]).to.eq('18') + expect(coords[1]).to.eq('26') + }) + + it('returns the correct coordinates when range midpoint is between 75_000 and 125_000', async () => { + const coords = await nftDescriptor.rangeLocation(100_000, 125_000) + expect(coords[0]).to.eq('21') + expect(coords[1]).to.eq('27') + }) + + it('returns the correct coordinates when range midpoint is above 125_000', async () => { + const coords = await nftDescriptor.rangeLocation(200_000, 100_000) + expect(coords[0]).to.eq('24') + expect(coords[1]).to.eq('27') + }) + + it('math does not overflow on max value', async () => { + const coords = await nftDescriptor.rangeLocation(887_272, 887_272) + expect(coords[0]).to.eq('24') + expect(coords[1]).to.eq('27') + }) + }) + + describe('#svgImage', () => { + let tokenId: number + let baseTokenAddress: string + let quoteTokenAddress: string + let baseTokenSymbol: string + let quoteTokenSymbol: string + let baseTokenDecimals: number + let quoteTokenDecimals: number + let flipRatio: boolean + let tickLower: number + let tickUpper: number + let tickCurrent: number + let tickSpacing: number + let fee: number + let poolAddress: string + + beforeEach(async () => { + tokenId = 123 + quoteTokenAddress = '0x1234567890123456789123456789012345678901' + baseTokenAddress = '0xabcdeabcdefabcdefabcdefabcdefabcdefabcdf' + quoteTokenSymbol = 'UNI' + baseTokenSymbol = 'WETH' + tickLower = -1000 + tickUpper = 2000 + tickCurrent = 40 + fee = 500 + baseTokenDecimals = await tokens[0].decimals() + quoteTokenDecimals = await tokens[1].decimals() + flipRatio = false + tickSpacing = TICK_SPACINGS[FeeAmount.MEDIUM] + poolAddress = `0x${'b'.repeat(40)}` + }) + + it('matches the current snapshot', async () => { + const svg = await nftDescriptor.generateSVGImage({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + + expect(svg).toMatchSnapshot() + fs.writeFileSync('./test/__snapshots__/NFTDescriptor.svg', svg) + }) + + it('returns a valid SVG', async () => { + const svg = await nftDescriptor.generateSVGImage({ + tokenId, + baseTokenAddress, + quoteTokenAddress, + baseTokenSymbol, + quoteTokenSymbol, + baseTokenDecimals, + quoteTokenDecimals, + flipRatio, + tickLower, + tickUpper, + tickCurrent, + tickSpacing, + fee, + poolAddress, + }) + expect(isSvg(svg)).to.eq(true) + }) + }) + + describe('#isRare', () => { + it('returns true sometimes', async () => { + expect(await nftDescriptor.isRare(1, `0x${'b'.repeat(40)}`)).to.eq(true) + }) + + it('returns false sometimes', async () => { + expect(await nftDescriptor.isRare(2, `0x${'b'.repeat(40)}`)).to.eq(false) + }) + }) + + function constructTokenMetadata( + tokenId: number, + quoteTokenAddress: string, + baseTokenAddress: string, + poolAddress: string, + quoteTokenSymbol: string, + baseTokenSymbol: string, + flipRatio: boolean, + tickLower: number, + tickUpper: number, + tickCurrent: number, + feeTier: string, + prices: string + ): { name: string; description: string } { + quoteTokenSymbol = quoteTokenSymbol.replace(/"/gi, '"') + baseTokenSymbol = baseTokenSymbol.replace(/"/gi, '"') + return { + name: `Uniswap - ${feeTier} - ${quoteTokenSymbol}/${baseTokenSymbol} - ${prices}`, + description: `This NFT represents a liquidity position in a Uniswap V3 ${quoteTokenSymbol}-${baseTokenSymbol} pool. The owner of this NFT can modify or redeem the position.\n\ +\nPool Address: ${poolAddress}\n${quoteTokenSymbol} Address: ${quoteTokenAddress.toLowerCase()}\n${baseTokenSymbol} Address: ${baseTokenAddress.toLowerCase()}\n\ +Fee Tier: ${feeTier}\nToken ID: ${tokenId}\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure token addresses match the expected tokens, as \ +token symbols may be imitated.`, + } + } +}) diff --git a/lib/uniswap/v3-periphery/test/NonfungiblePositionManager.spec.ts b/lib/uniswap/v3-periphery/test/NonfungiblePositionManager.spec.ts new file mode 100644 index 0000000..98215ad --- /dev/null +++ b/lib/uniswap/v3-periphery/test/NonfungiblePositionManager.spec.ts @@ -0,0 +1,1304 @@ +import { abi as IUniswapV3PoolABI } from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json' +import { Fixture } from 'ethereum-waffle' +import { BigNumberish, constants, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { + IUniswapV3Factory, + IWETH9, + MockTimeNonfungiblePositionManager, + NonfungiblePositionManagerPositionsGasTest, + SwapRouter, + TestERC20, + TestPositionNFTOwner, +} from '../typechain' +import completeFixture from './shared/completeFixture' +import { computePoolAddress } from './shared/computePoolAddress' +import { FeeAmount, MaxUint128, TICK_SPACINGS } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import { expect } from './shared/expect' +import { extractJSONFromURI } from './shared/extractJSONFromURI' +import getPermitNFTSignature from './shared/getPermitNFTSignature' +import { encodePath } from './shared/path' +import poolAtAddress from './shared/poolAtAddress' +import snapshotGasCost from './shared/snapshotGasCost' +import { getMaxTick, getMinTick } from './shared/ticks' +import { sortedTokens } from './shared/tokenSort' + +describe('NonfungiblePositionManager', () => { + let wallets: Wallet[] + let wallet: Wallet, other: Wallet + + const nftFixture: Fixture<{ + nft: MockTimeNonfungiblePositionManager + factory: IUniswapV3Factory + tokens: [TestERC20, TestERC20, TestERC20] + weth9: IWETH9 + router: SwapRouter + }> = async (wallets, provider) => { + const { weth9, factory, tokens, nft, router } = await completeFixture(wallets, provider) + + // approve & fund wallets + for (const token of tokens) { + await token.approve(nft.address, constants.MaxUint256) + await token.connect(other).approve(nft.address, constants.MaxUint256) + await token.transfer(other.address, expandTo18Decimals(1_000_000)) + } + + return { + nft, + factory, + tokens, + weth9, + router, + } + } + + let factory: IUniswapV3Factory + let nft: MockTimeNonfungiblePositionManager + let tokens: [TestERC20, TestERC20, TestERC20] + let weth9: IWETH9 + let router: SwapRouter + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + wallets = await (ethers as any).getSigners() + ;[wallet, other] = wallets + + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ nft, factory, tokens, weth9, router } = await loadFixture(nftFixture)) + }) + + it('bytecode size', async () => { + expect(((await nft.provider.getCode(nft.address)).length - 2) / 2).to.matchSnapshot() + }) + + describe('#createAndInitializePoolIfNecessary', () => { + it('creates the pool at the expected address', async () => { + const expectedAddress = computePoolAddress( + factory.address, + [tokens[0].address, tokens[1].address], + FeeAmount.MEDIUM + ) + const code = await wallet.provider.getCode(expectedAddress) + expect(code).to.eq('0x') + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + const codeAfter = await wallet.provider.getCode(expectedAddress) + expect(codeAfter).to.not.eq('0x') + }) + + it('is payable', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1), + { value: 1 } + ) + }) + + it('works if pool is created but not initialized', async () => { + const expectedAddress = computePoolAddress( + factory.address, + [tokens[0].address, tokens[1].address], + FeeAmount.MEDIUM + ) + await factory.createPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + const code = await wallet.provider.getCode(expectedAddress) + expect(code).to.not.eq('0x') + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(2, 1) + ) + }) + + it('works if pool is created and initialized', async () => { + const expectedAddress = computePoolAddress( + factory.address, + [tokens[0].address, tokens[1].address], + FeeAmount.MEDIUM + ) + await factory.createPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + const pool = new ethers.Contract(expectedAddress, IUniswapV3PoolABI, wallet) + + await pool.initialize(encodePriceSqrt(3, 1)) + const code = await wallet.provider.getCode(expectedAddress) + expect(code).to.not.eq('0x') + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(4, 1) + ) + }) + + it('could theoretically use eth via multicall', async () => { + const [token0, token1] = sortedTokens(weth9, tokens[0]) + + const createAndInitializePoolIfNecessaryData = nft.interface.encodeFunctionData( + 'createAndInitializePoolIfNecessary', + [token0.address, token1.address, FeeAmount.MEDIUM, encodePriceSqrt(1, 1)] + ) + + await nft.multicall([createAndInitializePoolIfNecessaryData], { value: expandTo18Decimals(1) }) + }) + + it('gas', async () => { + await snapshotGasCost( + nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + ) + }) + }) + + describe('#mint', () => { + it('fails if pool does not exist', async () => { + await expect( + nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + recipient: wallet.address, + deadline: 1, + fee: FeeAmount.MEDIUM, + }) + ).to.be.reverted + }) + + it('fails if cannot transfer', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + await tokens[0].approve(nft.address, 0) + await expect( + nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + recipient: wallet.address, + deadline: 1, + }) + ).to.be.revertedWith('STF') + }) + + it('creates a token', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 15, + amount1Desired: 15, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + expect(await nft.balanceOf(other.address)).to.eq(1) + expect(await nft.tokenOfOwnerByIndex(other.address, 0)).to.eq(1) + const { + fee, + token0, + token1, + tickLower, + tickUpper, + liquidity, + tokensOwed0, + tokensOwed1, + feeGrowthInside0LastX128, + feeGrowthInside1LastX128, + } = await nft.positions(1) + expect(token0).to.eq(tokens[0].address) + expect(token1).to.eq(tokens[1].address) + expect(fee).to.eq(FeeAmount.MEDIUM) + expect(tickLower).to.eq(getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + expect(tickUpper).to.eq(getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + expect(liquidity).to.eq(15) + expect(tokensOwed0).to.eq(0) + expect(tokensOwed1).to.eq(0) + expect(feeGrowthInside0LastX128).to.eq(0) + expect(feeGrowthInside1LastX128).to.eq(0) + }) + + it('can use eth via multicall', async () => { + const [token0, token1] = sortedTokens(weth9, tokens[0]) + + // remove any approval + await weth9.approve(nft.address, 0) + + const createAndInitializeData = nft.interface.encodeFunctionData('createAndInitializePoolIfNecessary', [ + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1), + ]) + + const mintData = nft.interface.encodeFunctionData('mint', [ + { + token0: token0.address, + token1: token1.address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }, + ]) + + const refundETHData = nft.interface.encodeFunctionData('refundETH') + + const balanceBefore = await wallet.getBalance() + const tx = await nft.multicall([createAndInitializeData, mintData, refundETHData], { + value: expandTo18Decimals(1), + }) + const receipt = await tx.wait() + const balanceAfter = await wallet.getBalance() + expect(balanceBefore).to.eq(balanceAfter.add(receipt.gasUsed.mul(tx.gasPrice)).add(100)) + }) + + it('emits an event') + + it('gas first mint for pool', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await snapshotGasCost( + nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + ) + }) + + it('gas first mint for pool using eth with zero refund', async () => { + const [token0, token1] = sortedTokens(weth9, tokens[0]) + await nft.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await snapshotGasCost( + nft.multicall( + [ + nft.interface.encodeFunctionData('mint', [ + { + token0: token0.address, + token1: token1.address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }, + ]), + nft.interface.encodeFunctionData('refundETH'), + ], + { value: 100 } + ) + ) + }) + + it('gas first mint for pool using eth with non-zero refund', async () => { + const [token0, token1] = sortedTokens(weth9, tokens[0]) + await nft.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await snapshotGasCost( + nft.multicall( + [ + nft.interface.encodeFunctionData('mint', [ + { + token0: token0.address, + token1: token1.address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }, + ]), + nft.interface.encodeFunctionData('refundETH'), + ], + { value: 1000 } + ) + ) + }) + + it('gas mint on same ticks', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + await snapshotGasCost( + nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + ) + }) + + it('gas mint for same pool, different ticks', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + await snapshotGasCost( + nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]) + TICK_SPACINGS[FeeAmount.MEDIUM], + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]) - TICK_SPACINGS[FeeAmount.MEDIUM], + fee: FeeAmount.MEDIUM, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + ) + }) + }) + + describe('#increaseLiquidity', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 1000, + amount1Desired: 1000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('increases position liquidity', async () => { + await nft.increaseLiquidity({ + tokenId: tokenId, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + const { liquidity } = await nft.positions(tokenId) + expect(liquidity).to.eq(1100) + }) + + it('emits an event') + + it('can be paid with ETH', async () => { + const [token0, token1] = sortedTokens(tokens[0], weth9) + + const tokenId = 1 + + await nft.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + const mintData = nft.interface.encodeFunctionData('mint', [ + { + token0: token0.address, + token1: token1.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }, + ]) + const refundETHData = nft.interface.encodeFunctionData('unwrapWETH9', [0, other.address]) + await nft.multicall([mintData, refundETHData], { value: expandTo18Decimals(1) }) + + const increaseLiquidityData = nft.interface.encodeFunctionData('increaseLiquidity', [ + { + tokenId: tokenId, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }, + ]) + await nft.multicall([increaseLiquidityData, refundETHData], { value: expandTo18Decimals(1) }) + }) + + it('gas', async () => { + await snapshotGasCost( + nft.increaseLiquidity({ + tokenId: tokenId, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + ) + }) + }) + + describe('#decreaseLiquidity', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('emits an event') + + it('fails if past deadline', async () => { + await nft.setTime(2) + await expect( + nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + ).to.be.revertedWith('Transaction too old') + }) + + it('cannot be called by other addresses', async () => { + await expect( + nft.decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + ).to.be.revertedWith('Not approved') + }) + + it('decreases position liquidity', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 25, amount0Min: 0, amount1Min: 0, deadline: 1 }) + const { liquidity } = await nft.positions(tokenId) + expect(liquidity).to.eq(75) + }) + + it('is payable', async () => { + await nft + .connect(other) + .decreaseLiquidity({ tokenId, liquidity: 25, amount0Min: 0, amount1Min: 0, deadline: 1 }, { value: 1 }) + }) + + it('accounts for tokens owed', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 25, amount0Min: 0, amount1Min: 0, deadline: 1 }) + const { tokensOwed0, tokensOwed1 } = await nft.positions(tokenId) + expect(tokensOwed0).to.eq(24) + expect(tokensOwed1).to.eq(24) + }) + + it('can decrease for all the liquidity', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 100, amount0Min: 0, amount1Min: 0, deadline: 1 }) + const { liquidity } = await nft.positions(tokenId) + expect(liquidity).to.eq(0) + }) + + it('cannot decrease for more than all the liquidity', async () => { + await expect( + nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 101, amount0Min: 0, amount1Min: 0, deadline: 1 }) + ).to.be.reverted + }) + + it('cannot decrease for more than the liquidity of the nft position', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 200, + amount1Desired: 200, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + await expect( + nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 101, amount0Min: 0, amount1Min: 0, deadline: 1 }) + ).to.be.reverted + }) + + it('gas partial decrease', async () => { + await snapshotGasCost( + nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + ) + }) + + it('gas complete decrease', async () => { + await snapshotGasCost( + nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 100, amount0Min: 0, amount1Min: 0, deadline: 1 }) + ) + }) + }) + + describe('#collect', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('emits an event') + + it('cannot be called by other addresses', async () => { + await expect( + nft.collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + ).to.be.revertedWith('Not approved') + }) + + it('cannot be called with 0 for both amounts', async () => { + await expect( + nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: 0, + amount1Max: 0, + }) + ).to.be.reverted + }) + + it('no op if no tokens are owed', async () => { + await expect( + nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + ) + .to.not.emit(tokens[0], 'Transfer') + .to.not.emit(tokens[1], 'Transfer') + }) + + it('transfers tokens owed from burn', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + const poolAddress = computePoolAddress(factory.address, [tokens[0].address, tokens[1].address], FeeAmount.MEDIUM) + await expect( + nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + ) + .to.emit(tokens[0], 'Transfer') + .withArgs(poolAddress, wallet.address, 49) + .to.emit(tokens[1], 'Transfer') + .withArgs(poolAddress, wallet.address, 49) + }) + + it('gas transfers both', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await snapshotGasCost( + nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + ) + }) + + it('gas transfers token0 only', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await snapshotGasCost( + nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: 0, + }) + ) + }) + + it('gas transfers token1 only', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await snapshotGasCost( + nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: 0, + amount1Max: MaxUint128, + }) + ) + }) + }) + + describe('#burn', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('emits an event') + + it('cannot be called by other addresses', async () => { + await expect(nft.burn(tokenId)).to.be.revertedWith('Not approved') + }) + + it('cannot be called while there is still liquidity', async () => { + await expect(nft.connect(other).burn(tokenId)).to.be.revertedWith('Not cleared') + }) + + it('cannot be called while there is still partial liquidity', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 50, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await expect(nft.connect(other).burn(tokenId)).to.be.revertedWith('Not cleared') + }) + + it('cannot be called while there is still tokens owed', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 100, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await expect(nft.connect(other).burn(tokenId)).to.be.revertedWith('Not cleared') + }) + + it('deletes the token', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 100, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + await nft.connect(other).burn(tokenId) + await expect(nft.positions(tokenId)).to.be.revertedWith('Invalid token ID') + }) + + it('gas', async () => { + await nft.connect(other).decreaseLiquidity({ tokenId, liquidity: 100, amount0Min: 0, amount1Min: 0, deadline: 1 }) + await nft.connect(other).collect({ + tokenId, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + await snapshotGasCost(nft.connect(other).burn(tokenId)) + }) + }) + + describe('#transferFrom', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('can only be called by authorized or owner', async () => { + await expect(nft.transferFrom(other.address, wallet.address, tokenId)).to.be.revertedWith( + 'ERC721: transfer caller is not owner nor approved' + ) + }) + + it('changes the owner', async () => { + await nft.connect(other).transferFrom(other.address, wallet.address, tokenId) + expect(await nft.ownerOf(tokenId)).to.eq(wallet.address) + }) + + it('removes existing approval', async () => { + await nft.connect(other).approve(wallet.address, tokenId) + expect(await nft.getApproved(tokenId)).to.eq(wallet.address) + await nft.transferFrom(other.address, wallet.address, tokenId) + expect(await nft.getApproved(tokenId)).to.eq(constants.AddressZero) + }) + + it('gas', async () => { + await snapshotGasCost(nft.connect(other).transferFrom(other.address, wallet.address, tokenId)) + }) + + it('gas comes from approved', async () => { + await nft.connect(other).approve(wallet.address, tokenId) + await snapshotGasCost(nft.transferFrom(other.address, wallet.address, tokenId)) + }) + }) + + describe('#permit', () => { + it('emits an event') + + describe('owned by eoa', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('changes the operator of the position and increments the nonce', async () => { + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await nft.permit(wallet.address, tokenId, 1, v, r, s) + expect((await nft.positions(tokenId)).nonce).to.eq(1) + expect((await nft.positions(tokenId)).operator).to.eq(wallet.address) + }) + + it('cannot be called twice with the same signature', async () => { + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await nft.permit(wallet.address, tokenId, 1, v, r, s) + await expect(nft.permit(wallet.address, tokenId, 1, v, r, s)).to.be.reverted + }) + + it('fails with invalid signature', async () => { + const { v, r, s } = await getPermitNFTSignature(wallet, nft, wallet.address, tokenId, 1) + await expect(nft.permit(wallet.address, tokenId, 1, v + 3, r, s)).to.be.revertedWith('Invalid signature') + }) + + it('fails with signature not from owner', async () => { + const { v, r, s } = await getPermitNFTSignature(wallet, nft, wallet.address, tokenId, 1) + await expect(nft.permit(wallet.address, tokenId, 1, v, r, s)).to.be.revertedWith('Unauthorized') + }) + + it('fails with expired signature', async () => { + await nft.setTime(2) + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await expect(nft.permit(wallet.address, tokenId, 1, v, r, s)).to.be.revertedWith('Permit expired') + }) + + it('gas', async () => { + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await snapshotGasCost(nft.permit(wallet.address, tokenId, 1, v, r, s)) + }) + }) + describe('owned by verifying contract', () => { + const tokenId = 1 + let testPositionNFTOwner: TestPositionNFTOwner + + beforeEach('deploy test owner and create a position', async () => { + testPositionNFTOwner = (await ( + await ethers.getContractFactory('TestPositionNFTOwner') + ).deploy()) as TestPositionNFTOwner + + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: testPositionNFTOwner.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('changes the operator of the position and increments the nonce', async () => { + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await testPositionNFTOwner.setOwner(other.address) + await nft.permit(wallet.address, tokenId, 1, v, r, s) + expect((await nft.positions(tokenId)).nonce).to.eq(1) + expect((await nft.positions(tokenId)).operator).to.eq(wallet.address) + }) + + it('fails if owner contract is owned by different address', async () => { + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await testPositionNFTOwner.setOwner(wallet.address) + await expect(nft.permit(wallet.address, tokenId, 1, v, r, s)).to.be.revertedWith('Unauthorized') + }) + + it('fails with signature not from owner', async () => { + const { v, r, s } = await getPermitNFTSignature(wallet, nft, wallet.address, tokenId, 1) + await testPositionNFTOwner.setOwner(other.address) + await expect(nft.permit(wallet.address, tokenId, 1, v, r, s)).to.be.revertedWith('Unauthorized') + }) + + it('fails with expired signature', async () => { + await nft.setTime(2) + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await testPositionNFTOwner.setOwner(other.address) + await expect(nft.permit(wallet.address, tokenId, 1, v, r, s)).to.be.revertedWith('Permit expired') + }) + + it('gas', async () => { + const { v, r, s } = await getPermitNFTSignature(other, nft, wallet.address, tokenId, 1) + await testPositionNFTOwner.setOwner(other.address) + await snapshotGasCost(nft.permit(wallet.address, tokenId, 1, v, r, s)) + }) + }) + }) + + describe('multicall exit', () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + async function exit({ + nft, + liquidity, + tokenId, + amount0Min, + amount1Min, + recipient, + }: { + nft: MockTimeNonfungiblePositionManager + tokenId: BigNumberish + liquidity: BigNumberish + amount0Min: BigNumberish + amount1Min: BigNumberish + recipient: string + }) { + const decreaseLiquidityData = nft.interface.encodeFunctionData('decreaseLiquidity', [ + { tokenId, liquidity, amount0Min, amount1Min, deadline: 1 }, + ]) + const collectData = nft.interface.encodeFunctionData('collect', [ + { + tokenId, + recipient, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }, + ]) + const burnData = nft.interface.encodeFunctionData('burn', [tokenId]) + + return nft.multicall([decreaseLiquidityData, collectData, burnData]) + } + + it('executes all the actions', async () => { + const pool = poolAtAddress( + computePoolAddress(factory.address, [tokens[0].address, tokens[1].address], FeeAmount.MEDIUM), + wallet + ) + await expect( + exit({ + nft: nft.connect(other), + tokenId, + liquidity: 100, + amount0Min: 0, + amount1Min: 0, + recipient: wallet.address, + }) + ) + .to.emit(pool, 'Burn') + .to.emit(pool, 'Collect') + }) + + it('gas', async () => { + await snapshotGasCost( + exit({ + nft: nft.connect(other), + tokenId, + liquidity: 100, + amount0Min: 0, + amount1Min: 0, + recipient: wallet.address, + }) + ) + }) + }) + + describe('#tokenURI', async () => { + const tokenId = 1 + beforeEach('create a position', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: other.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + }) + + it('reverts for invalid token id', async () => { + await expect(nft.tokenURI(tokenId + 1)).to.be.reverted + }) + + it('returns a data URI with correct mime type', async () => { + expect(await nft.tokenURI(tokenId)).to.match(/data:application\/json;base64,.+/) + }) + + it('content is valid JSON and structure', async () => { + const content = extractJSONFromURI(await nft.tokenURI(tokenId)) + expect(content).to.haveOwnProperty('name').is.a('string') + expect(content).to.haveOwnProperty('description').is.a('string') + expect(content).to.haveOwnProperty('image').is.a('string') + }) + }) + + describe('fees accounting', () => { + beforeEach('create two positions', async () => { + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + // nft 1 earns 25% of fees + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + recipient: wallet.address, + }) + // nft 2 earns 75% of fees + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + + amount0Desired: 300, + amount1Desired: 300, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + recipient: wallet.address, + }) + }) + + describe('10k of token0 fees collect', () => { + beforeEach('swap for ~10k of fees', async () => { + const swapAmount = 3_333_333 + await tokens[0].approve(router.address, swapAmount) + await router.exactInput({ + recipient: wallet.address, + deadline: 1, + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + amountIn: swapAmount, + amountOutMinimum: 0, + }) + }) + it('expected amounts', async () => { + const { amount0: nft1Amount0, amount1: nft1Amount1 } = await nft.callStatic.collect({ + tokenId: 1, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + const { amount0: nft2Amount0, amount1: nft2Amount1 } = await nft.callStatic.collect({ + tokenId: 2, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + expect(nft1Amount0).to.eq(2501) + expect(nft1Amount1).to.eq(0) + expect(nft2Amount0).to.eq(7503) + expect(nft2Amount1).to.eq(0) + }) + + it('actually collected', async () => { + const poolAddress = computePoolAddress( + factory.address, + [tokens[0].address, tokens[1].address], + FeeAmount.MEDIUM + ) + + await expect( + nft.collect({ + tokenId: 1, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + ) + .to.emit(tokens[0], 'Transfer') + .withArgs(poolAddress, wallet.address, 2501) + .to.not.emit(tokens[1], 'Transfer') + await expect( + nft.collect({ + tokenId: 2, + recipient: wallet.address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + ) + .to.emit(tokens[0], 'Transfer') + .withArgs(poolAddress, wallet.address, 7503) + .to.not.emit(tokens[1], 'Transfer') + }) + }) + }) + + describe('#positions', async () => { + it('gas', async () => { + const positionsGasTestFactory = await ethers.getContractFactory('NonfungiblePositionManagerPositionsGasTest') + const positionsGasTest = (await positionsGasTestFactory.deploy( + nft.address + )) as NonfungiblePositionManagerPositionsGasTest + + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: other.address, + amount0Desired: 15, + amount1Desired: 15, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + await snapshotGasCost(positionsGasTest.getGasCostOfPositions(1)) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/NonfungibleTokenPositionDescriptor.spec.ts b/lib/uniswap/v3-periphery/test/NonfungibleTokenPositionDescriptor.spec.ts new file mode 100644 index 0000000..783ce4a --- /dev/null +++ b/lib/uniswap/v3-periphery/test/NonfungibleTokenPositionDescriptor.spec.ts @@ -0,0 +1,217 @@ +import { constants, Wallet } from 'ethers' +import { waffle, ethers } from 'hardhat' +import { expect } from './shared/expect' +import { Fixture } from 'ethereum-waffle' +import { NonfungibleTokenPositionDescriptor, MockTimeNonfungiblePositionManager, TestERC20 } from '../typechain' +import completeFixture from './shared/completeFixture' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { FeeAmount, TICK_SPACINGS } from './shared/constants' +import { getMaxTick, getMinTick } from './shared/ticks' +import { sortedTokens } from './shared/tokenSort' +import { extractJSONFromURI } from './shared/extractJSONFromURI' + +const DAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F' +const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' +const USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' +const TBTC = '0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa' +const WBTC = '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599' + +describe('NonfungibleTokenPositionDescriptor', () => { + let wallets: Wallet[] + + const nftPositionDescriptorCompleteFixture: Fixture<{ + nftPositionDescriptor: NonfungibleTokenPositionDescriptor + tokens: [TestERC20, TestERC20, TestERC20] + nft: MockTimeNonfungiblePositionManager + }> = async (wallets, provider) => { + const { factory, nft, router, nftDescriptor } = await completeFixture(wallets, provider) + const tokenFactory = await ethers.getContractFactory('TestERC20') + const tokens: [TestERC20, TestERC20, TestERC20] = [ + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, // do not use maxu256 to avoid overflowing + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + ] + tokens.sort((a, b) => (a.address.toLowerCase() < b.address.toLowerCase() ? -1 : 1)) + + return { + nftPositionDescriptor: nftDescriptor, + tokens, + nft, + } + } + + let nftPositionDescriptor: NonfungibleTokenPositionDescriptor + let tokens: [TestERC20, TestERC20, TestERC20] + let nft: MockTimeNonfungiblePositionManager + let weth9: TestERC20 + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + wallets = await (ethers as any).getSigners() + + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ tokens, nft, nftPositionDescriptor } = await loadFixture(nftPositionDescriptorCompleteFixture)) + const tokenFactory = await ethers.getContractFactory('TestERC20') + weth9 = tokenFactory.attach(await nftPositionDescriptor.WETH9()) as TestERC20 + }) + + describe('#tokenRatioPriority', () => { + it('returns -100 for WETH9', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(weth9.address, 1)).to.eq(-100) + }) + + it('returns 200 for USDC', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(USDC, 1)).to.eq(300) + }) + + it('returns 100 for DAI', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(DAI, 1)).to.eq(100) + }) + + it('returns 150 for USDT', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(USDT, 1)).to.eq(200) + }) + + it('returns -200 for TBTC', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(TBTC, 1)).to.eq(-200) + }) + + it('returns -250 for WBTC', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(WBTC, 1)).to.eq(-300) + }) + + it('returns 0 for any non-ratioPriority token', async () => { + expect(await nftPositionDescriptor.tokenRatioPriority(tokens[0].address, 1)).to.eq(0) + }) + }) + + describe('#flipRatio', () => { + it('returns false if neither token has priority ordering', async () => { + expect(await nftPositionDescriptor.flipRatio(tokens[0].address, tokens[2].address, 1)).to.eq(false) + }) + + it('returns true if both tokens are numerators but token0 has a higher priority ordering', async () => { + expect(await nftPositionDescriptor.flipRatio(USDC, DAI, 1)).to.eq(true) + }) + + it('returns true if both tokens are denominators but token1 has lower priority ordering', async () => { + expect(await nftPositionDescriptor.flipRatio(weth9.address, WBTC, 1)).to.eq(true) + }) + + it('returns true if token0 is a numerator and token1 is a denominator', async () => { + expect(await nftPositionDescriptor.flipRatio(DAI, WBTC, 1)).to.eq(true) + }) + + it('returns false if token1 is a numerator and token0 is a denominator', async () => { + expect(await nftPositionDescriptor.flipRatio(WBTC, DAI, 1)).to.eq(false) + }) + }) + + describe('#tokenURI', () => { + it('displays ETH as token symbol for WETH token', async () => { + const [token0, token1] = sortedTokens(weth9, tokens[1]) + await nft.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + await weth9.approve(nft.address, 100) + await tokens[1].approve(nft.address, 100) + await nft.mint({ + token0: token0.address, + token1: token1.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallets[0].address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + + const metadata = extractJSONFromURI(await nft.tokenURI(1)) + expect(metadata.name).to.match(/(\sETH\/TEST|TEST\/ETH)/) + expect(metadata.description).to.match(/(TEST-ETH|\sETH-TEST)/) + expect(metadata.description).to.match(/(\nETH\sAddress)/) + }) + + it('displays returned token symbols when neither token is WETH ', async () => { + const [token0, token1] = sortedTokens(tokens[2], tokens[1]) + await nft.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + await tokens[1].approve(nft.address, 100) + await tokens[2].approve(nft.address, 100) + await nft.mint({ + token0: token0.address, + token1: token1.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallets[0].address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + + const metadata = extractJSONFromURI(await nft.tokenURI(1)) + expect(metadata.name).to.match(/TEST\/TEST/) + expect(metadata.description).to.match(/TEST-TEST/) + }) + + it('can render a different label for native currencies', async () => { + const [token0, token1] = sortedTokens(weth9, tokens[1]) + await nft.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + await weth9.approve(nft.address, 100) + await tokens[1].approve(nft.address, 100) + await nft.mint({ + token0: token0.address, + token1: token1.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallets[0].address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + + const nftDescriptorLibraryFactory = await ethers.getContractFactory('NFTDescriptor') + const nftDescriptorLibrary = await nftDescriptorLibraryFactory.deploy() + const positionDescriptorFactory = await ethers.getContractFactory('NonfungibleTokenPositionDescriptor', { + libraries: { + NFTDescriptor: nftDescriptorLibrary.address, + }, + }) + const nftDescriptor = (await positionDescriptorFactory.deploy( + weth9.address, + // 'FUNNYMONEY' as a bytes32 string + '0x46554e4e594d4f4e455900000000000000000000000000000000000000000000' + )) as NonfungibleTokenPositionDescriptor + + const metadata = extractJSONFromURI(await nftDescriptor.tokenURI(nft.address, 1)) + expect(metadata.name).to.match(/(\sFUNNYMONEY\/TEST|TEST\/FUNNYMONEY)/) + expect(metadata.description).to.match(/(TEST-FUNNYMONEY|\sFUNNYMONEY-TEST)/) + expect(metadata.description).to.match(/(\nFUNNYMONEY\sAddress)/) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/OracleLibrary.spec.ts b/lib/uniswap/v3-periphery/test/OracleLibrary.spec.ts new file mode 100644 index 0000000..d15864e --- /dev/null +++ b/lib/uniswap/v3-periphery/test/OracleLibrary.spec.ts @@ -0,0 +1,675 @@ +import { expect } from 'chai' +import { ethers, waffle } from 'hardhat' +import { BigNumber, BigNumberish, constants, ContractFactory, Contract } from 'ethers' +import { OracleTest, TestERC20 } from '../typechain' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import snapshotGasCost from './shared/snapshotGasCost' + +describe('OracleLibrary', () => { + let loadFixture: ReturnType + let tokens: TestERC20[] + let oracle: OracleTest + + const BN0 = BigNumber.from(0) + + const oracleTestFixture = async () => { + const tokenFactory = await ethers.getContractFactory('TestERC20') + const tokens: [TestERC20, TestERC20, TestERC20] = [ + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, // do not use maxu256 to avoid overflowing + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + ] + + tokens.sort((a, b) => (a.address.toLowerCase() < b.address.toLowerCase() ? -1 : 1)) + + const oracleFactory = await ethers.getContractFactory('OracleTest') + const oracle = await oracleFactory.deploy() + + return { + tokens: tokens as TestERC20[], + oracle: oracle as OracleTest, + } + } + + before('create fixture loader', async () => { + loadFixture = waffle.createFixtureLoader(await (ethers as any).getSigners()) + }) + + beforeEach('deploy fixture', async () => { + const fixtures = await loadFixture(oracleTestFixture) + tokens = fixtures['tokens'] + oracle = fixtures['oracle'] + }) + + describe('#consult', () => { + let mockObservableFactory: ContractFactory + + before('create mockObservableFactory', async () => { + mockObservableFactory = await ethers.getContractFactory('MockObservable') + }) + + it('reverts when period is 0', async () => { + await expect(oracle.consult(oracle.address, 0)).to.be.revertedWith('BP') + }) + + it('correct output when tick is 0', async () => { + const period = 3 + const secondsPerLiqCumulatives: [BigNumberish, BigNumberish] = [10, 20] + const mockObservable = await observableWith({ + period, + tickCumulatives: [12, 12], + secondsPerLiqCumulatives, + }) + const { arithmeticMeanTick, harmonicMeanLiquidity } = await oracle.consult(mockObservable.address, period) + + expect(arithmeticMeanTick).to.equal(0) + expect(harmonicMeanLiquidity).to.equal(calculateHarmonicAvgLiq(period, secondsPerLiqCumulatives)) + }) + + it('correct rounding for .5 negative tick', async () => { + const period = 4 + + const secondsPerLiqCumulatives: [BigNumberish, BigNumberish] = [10, 15] + const mockObservable = await observableWith({ + period, + tickCumulatives: [-10, -12], + secondsPerLiqCumulatives, + }) + + const { arithmeticMeanTick, harmonicMeanLiquidity } = await oracle.consult(mockObservable.address, period) + + // Always round to negative infinity + // In this case, we need to subtract one because integer division rounds to 0 + expect(arithmeticMeanTick).to.equal(-1) + expect(harmonicMeanLiquidity).to.equal(calculateHarmonicAvgLiq(period, secondsPerLiqCumulatives)) + }) + + it('correct output for liquidity overflow', async () => { + const period = 1 + + const secondsPerLiqCumulatives: [BigNumberish, BigNumberish] = [10, 11] + const mockObservable = await observableWith({ + period, + tickCumulatives: [12, 12], + secondsPerLiqCumulatives, + }) + + const { arithmeticMeanTick, harmonicMeanLiquidity } = await oracle.consult(mockObservable.address, period) + + expect(arithmeticMeanTick).to.equal(0) + // Make sure liquidity doesn't overflow uint128 + expect(harmonicMeanLiquidity).to.equal(BigNumber.from(2).pow(128).sub(1)) + }) + + function calculateHarmonicAvgLiq(period: number, secondsPerLiqCumulatives: [BigNumberish, BigNumberish]) { + const [secondsPerLiq0, secondsPerLiq1] = secondsPerLiqCumulatives.map(BigNumber.from) + const delta = secondsPerLiq1.sub(secondsPerLiq0) + + const maxUint160 = BigNumber.from(2).pow(160).sub(1) + return maxUint160.mul(period).div(delta.shl(32)) + } + + function observableWith({ + period, + tickCumulatives, + secondsPerLiqCumulatives, + }: { + period: number + tickCumulatives: [BigNumberish, BigNumberish] + secondsPerLiqCumulatives: [BigNumberish, BigNumberish] + }) { + return mockObservableFactory.deploy( + [period, 0], + tickCumulatives.map(BigNumber.from), + secondsPerLiqCumulatives.map(BigNumber.from) + ) + } + }) + + describe('#getQuoteAtTick', () => { + // sanity check + it('token0: returns correct value when tick = 0', async () => { + const quoteAmount = await oracle.getQuoteAtTick(BN0, expandTo18Decimals(1), tokens[0].address, tokens[1].address) + + expect(quoteAmount).to.equal(expandTo18Decimals(1)) + }) + + // sanity check + it('token1: returns correct value when tick = 0', async () => { + const quoteAmount = await oracle.getQuoteAtTick(BN0, expandTo18Decimals(1), tokens[1].address, tokens[0].address) + + expect(quoteAmount).to.equal(expandTo18Decimals(1)) + }) + + it('token0: returns correct value when at min tick | 0 < sqrtRatioX96 <= type(uint128).max', async () => { + const quoteAmount = await oracle.getQuoteAtTick( + BigNumber.from(-887272), + BigNumber.from(2).pow(128).sub(1), + tokens[0].address, + tokens[1].address + ) + expect(quoteAmount).to.equal(BigNumber.from('1')) + }) + + it('token1: returns correct value when at min tick | 0 < sqrtRatioX96 <= type(uint128).max', async () => { + const quoteAmount = await oracle.getQuoteAtTick( + BigNumber.from(-887272), + BigNumber.from(2).pow(128).sub(1), + tokens[1].address, + tokens[0].address + ) + expect(quoteAmount).to.equal( + BigNumber.from('115783384738768196242144082653949453838306988932806144552194799290216044976282') + ) + }) + + it('token0: returns correct value when at max tick | sqrtRatioX96 > type(uint128).max', async () => { + const quoteAmount = await oracle.getQuoteAtTick( + BigNumber.from(887272), + BigNumber.from(2).pow(128).sub(1), + tokens[0].address, + tokens[1].address + ) + expect(quoteAmount).to.equal( + BigNumber.from('115783384785599357996676985412062652720342362943929506828539444553934033845703') + ) + }) + + it('token1: returns correct value when at max tick | sqrtRatioX96 > type(uint128).max', async () => { + const quoteAmount = await oracle.getQuoteAtTick( + BigNumber.from(887272), + BigNumber.from(2).pow(128).sub(1), + tokens[1].address, + tokens[0].address + ) + expect(quoteAmount).to.equal(BigNumber.from('1')) + }) + + it('gas test', async () => { + await snapshotGasCost( + oracle.getGasCostOfGetQuoteAtTick( + BigNumber.from(10), + expandTo18Decimals(1), + tokens[0].address, + tokens[1].address + ) + ) + }) + }) + + describe('#getOldestObservationSecondsAgo', () => { + let mockObservationsFactory: ContractFactory + + // some empty tick values as this function does not use them + const emptySPL = [0, 0, 0, 0] + const emptyTickCumulatives = [0, 0, 0, 0] + const emptyTick = 0 + const emptyLiquidity = 0 + + // helper function to run each test case identically + const runOldestObservationsTest = async ( + blockTimestamps: number[], + initializeds: boolean[], + observationCardinality: number, + observationIndex: number + ) => { + const mockObservations = await mockObservationsFactory.deploy( + blockTimestamps, + emptyTickCumulatives, + emptySPL, + initializeds, + emptyTick, + observationCardinality, + observationIndex, + false, + emptyLiquidity + ) + + var result = await oracle.getOldestObservationSecondsAgo(mockObservations.address) + + //calculate seconds ago + var secondsAgo + if (initializeds[(observationIndex + 1) % observationCardinality]) { + secondsAgo = result['currentTimestamp'] - blockTimestamps[(observationIndex + 1) % observationCardinality] + } else { + secondsAgo = result['currentTimestamp'] - blockTimestamps[0] + } + + if (secondsAgo < 0) { + secondsAgo += 2 ** 32 + } + + expect(result['secondsAgo']).to.equal(secondsAgo) + } + + before('create mockObservationsFactory', async () => { + mockObservationsFactory = await ethers.getContractFactory('MockObservations') + }) + + it('fetches the oldest timestamp from the slot after observationIndex', async () => { + // set up test case + const blockTimestamps = [2, 3, 1, 0] + const initializeds = [true, true, true, false] + const observationCardinality = 3 + const observationIndex = 1 + + // run test + await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex) + }) + + it('loops to fetches the oldest timestamp from index 0', async () => { + // set up test case + const blockTimestamps = [1, 2, 3, 0] + const initializeds = [true, true, true, false] + const observationCardinality = 3 + const observationIndex = 2 + + // run test + await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex) + }) + + it('fetches from index 0 if the next index is uninitialized', async () => { + // set up test case + const blockTimestamps = [1, 2, 0, 0] + const initializeds = [true, true, false, false] + const observationCardinality = 4 + const observationIndex = 1 + + // run test + await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex) + }) + + it('reverts if the pool is not initialized', async () => { + const blockTimestamps = [0, 0, 0, 0] + const initializeds = [false, false, false, false] + const observationCardinality = 0 + const observationIndex = 0 + const mockObservations = await mockObservationsFactory.deploy( + blockTimestamps, + emptyTickCumulatives, + emptySPL, + initializeds, + emptyTick, + observationCardinality, + observationIndex, + false, + emptyLiquidity + ) + + await expect(oracle.getOldestObservationSecondsAgo(mockObservations.address)).to.be.revertedWith('NI') + }) + + it('fetches the correct timestamp when the timestamps overflow', async () => { + // set up test case + const maxUint32 = 2 ** 32 - 1 + const blockTimestamps = [maxUint32, 3, maxUint32 - 2, 0] + const initializeds = [true, true, true, false] + const observationCardinality = 3 + const observationIndex = 1 + + // run test + await runOldestObservationsTest(blockTimestamps, initializeds, observationCardinality, observationIndex) + }) + }) + + describe('#getBlockStartingTickAndLiquidity', () => { + let mockObservationsFactory: ContractFactory + let mockObservations: Contract + let blockTimestamps: number[] + let tickCumulatives: number[] + let liquidityValues: BigNumber[] + let initializeds: boolean[] + let slot0Tick: number + let observationCardinality: number + let observationIndex: number + let lastObservationCurrentTimestamp: boolean + let liquidity: number + + before('create mockObservationsFactory', async () => { + mockObservationsFactory = await ethers.getContractFactory('MockObservations') + }) + + const deployMockObservationsContract = async () => { + mockObservations = await mockObservationsFactory.deploy( + blockTimestamps, + tickCumulatives, + liquidityValues, + initializeds, + slot0Tick, + observationCardinality, + observationIndex, + lastObservationCurrentTimestamp, + liquidity + ) + } + + it('reverts if the pool is not initialized', async () => { + blockTimestamps = [0, 0, 0, 0] + tickCumulatives = [0, 0, 0, 0] + liquidityValues = [BN0, BN0, BN0, BN0] + initializeds = [false, false, false, false] + slot0Tick = 0 + observationCardinality = 0 + observationIndex = 0 + lastObservationCurrentTimestamp = false + liquidity = 0 + + await deployMockObservationsContract() + + await expect(oracle.getBlockStartingTickAndLiquidity(mockObservations.address)).to.be.revertedWith('NEO') + }) + + it('returns the tick and liquidity in storage if the latest observation was in a previous block', async () => { + blockTimestamps = [1, 3, 4, 0] + // 0 + // 8: 0 + (4*(3-1)) + // 13: 8 + (5*(4-3)) + tickCumulatives = [0, 8, 13, 0] + // 0 + // (1): 0 + ((3-1)*2**128)/5000 + // (1) + ((4-3)*2**128)/7000 + liquidityValues = [ + BN0, + BigNumber.from('136112946768375385385349842972707284'), + BigNumber.from('184724713471366594451546215462959885'), + BN0, + ] + initializeds = [true, true, true, false] + observationCardinality = 3 + observationIndex = 2 + slot0Tick = 6 + lastObservationCurrentTimestamp = false + liquidity = 10000 + + await deployMockObservationsContract() + + var result = await oracle.getBlockStartingTickAndLiquidity(mockObservations.address) + expect(result[0]).to.equal(slot0Tick) + expect(result[1]).to.equal(liquidity) + }) + + it('reverts if it needs 2 observations and doesnt have them', async () => { + blockTimestamps = [1, 0, 0, 0] + tickCumulatives = [8, 0, 0, 0] + liquidityValues = [BigNumber.from('136112946768375385385349842972707284'), BN0, BN0, BN0] + initializeds = [true, false, false, false] + observationCardinality = 1 + observationIndex = 0 + slot0Tick = 4 + lastObservationCurrentTimestamp = true + liquidity = 10000 + + await deployMockObservationsContract() + + await expect(oracle.getBlockStartingTickAndLiquidity(mockObservations.address)).to.be.revertedWith('NEO') + }) + + it('reverts if the prior observation needed is not initialized', async () => { + blockTimestamps = [1, 0, 0, 0] + observationCardinality = 2 + observationIndex = 0 + liquidityValues = [BigNumber.from('136112946768375385385349842972707284'), BN0, BN0, BN0] + initializeds = [true, false, false, false] + tickCumulatives = [8, 0, 0, 0] + slot0Tick = 4 + lastObservationCurrentTimestamp = true + + await deployMockObservationsContract() + + await expect(oracle.getBlockStartingTickAndLiquidity(mockObservations.address)).to.be.revertedWith('ONI') + }) + + it('calculates the prior tick and liquidity from the prior observations', async () => { + blockTimestamps = [9, 5, 8, 0] + observationCardinality = 3 + observationIndex = 0 + initializeds = [true, true, true, false] + // 99: 95 + (4*1) + // 80: 72 + (4*2) + // 95: 80 + (5*3) + tickCumulatives = [99, 80, 95, 0] + // prev: 784724713471366594451546215462959885 + // (3): (2) + (1*2**128)/13212 + // (1): prev + (2*2**128)/12345 + // (2): (1) + (3*2**128)/10238 + liquidityValues = [ + BigNumber.from('965320616647837491242414421221086683'), + BigNumber.from('839853488995212437053956034406948254'), + BigNumber.from('939565063595995342933046073701273770'), + BN0, + ] + slot0Tick = 3 + lastObservationCurrentTimestamp = true + + await deployMockObservationsContract() + + var result = await oracle.getBlockStartingTickAndLiquidity(mockObservations.address) + + var actualStartingTick = (tickCumulatives[0] - tickCumulatives[2]) / (blockTimestamps[0] - blockTimestamps[2]) + expect(result[0]).to.equal(actualStartingTick) + + var actualStartingLiquidity = 13212 // see comments above + expect(result[1]).to.equal(actualStartingLiquidity) + }) + }) + + describe('#getWeightedArithmeticMeanTick', () => { + it('single observation returns average tick', async () => { + const observation = { tick: 10, weight: 10 } + + const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation]) + + expect(oracleTick).to.equal(10) + }) + + it('multiple observations with same weight result in average across tiers', async () => { + const observation1 = { tick: 10, weight: 10 } + const observation2 = { tick: 20, weight: 10 } + + const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation1, observation2]) + + expect(oracleTick).to.equal(15) + }) + + it('multiple observations with different weights are weighted correctly', async () => { + const observation2 = { tick: 20, weight: 15 } + const observation1 = { tick: 10, weight: 10 } + + const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation1, observation2]) + + expect(oracleTick).to.equal(16) + }) + + it('correct rounding for .5 negative tick', async () => { + const observation1 = { tick: -10, weight: 10 } + const observation2 = { tick: -11, weight: 10 } + + const oracleTick = await oracle.getWeightedArithmeticMeanTick([observation1, observation2]) + + expect(oracleTick).to.equal(-11) + }) + }) + describe('#getChainedPrice', () => { + let ticks: number[] + + it('fails with discrepant length', async () => { + const tokenAddresses = [tokens[0].address, tokens[2].address] + ticks = [5, 5] + + expect(oracle.getChainedPrice(tokenAddresses, ticks)).to.be.revertedWith('DL') + }) + it('add two positive ticks, sorted order', async () => { + const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address] + ticks = [5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(10) + }) + it('add one positive and one negative tick, sorted order', async () => { + const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address] + ticks = [5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add one negative and one positive tick, sorted order', async () => { + const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address] + ticks = [-5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add two negative ticks, sorted order', async () => { + const tokenAddresses = [tokens[0].address, tokens[1].address, tokens[2].address] + ticks = [-5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(-10) + }) + + it('add two positive ticks, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address] + ticks = [5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add one positive tick and one negative tick, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address] + ticks = [5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(10) + }) + it('add one negative tick and one positive tick, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address] + ticks = [-5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(-10) + }) + it('add two negative ticks, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[0].address, tokens[2].address, tokens[1].address] + ticks = [-5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + + it('add two positive ticks, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address] + ticks = [5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add one positive tick and one negative tick, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address] + ticks = [5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(-10) + }) + it('add one negative tick and one positive tick, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address] + ticks = [-5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(10) + }) + it('add two negative ticks, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[1].address, tokens[0].address, tokens[2].address] + ticks = [-5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + + it('add two positive ticks, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address] + ticks = [5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add one positive tick and one negative tick, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address] + ticks = [5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(10) + }) + it('add one negative tick and one positive tick, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address] + ticks = [-5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(-10) + }) + it('add two negative ticks, token0/token1 + token1/token0', async () => { + const tokenAddresses = [tokens[1].address, tokens[2].address, tokens[0].address] + ticks = [-5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + + it('add two positive ticks, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address] + ticks = [5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add one positive tick and one negative tick, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address] + ticks = [5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(-10) + }) + it('add one negative tick and one positive tick, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address] + ticks = [-5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(10) + }) + it('add two negative ticks, token1/token0 + token0/token1', async () => { + const tokenAddresses = [tokens[2].address, tokens[0].address, tokens[1].address] + ticks = [-5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + + it('add two positive ticks, token1/token0 + token1/token0', async () => { + const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address] + ticks = [5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(-10) + }) + it('add one positive tick and one negative tick, token1/token0 + token1/token0', async () => { + const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address] + ticks = [5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add one negative tick and one positive tick, token1/token0 + token1/token0', async () => { + const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address] + ticks = [-5, 5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(0) + }) + it('add two negative ticks, token1/token0 + token1/token0', async () => { + const tokenAddresses = [tokens[2].address, tokens[1].address, tokens[0].address] + ticks = [-5, -5] + const oracleTick = await oracle.getChainedPrice(tokenAddresses, ticks) + + expect(oracleTick).to.equal(10) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/PairFlash.spec.ts b/lib/uniswap/v3-periphery/test/PairFlash.spec.ts new file mode 100644 index 0000000..b6084ed --- /dev/null +++ b/lib/uniswap/v3-periphery/test/PairFlash.spec.ts @@ -0,0 +1,168 @@ +import { ethers, waffle } from 'hardhat' +import { BigNumber, constants, Contract, ContractTransaction } from 'ethers' +import { + IWETH9, + MockTimeNonfungiblePositionManager, + MockTimeSwapRouter, + PairFlash, + IUniswapV3Pool, + TestERC20, + TestERC20Metadata, + IUniswapV3Factory, + NFTDescriptor, + Quoter, + SwapRouter, +} from '../typechain' +import completeFixture from './shared/completeFixture' +import { FeeAmount, MaxUint128, TICK_SPACINGS } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import snapshotGasCost from './shared/snapshotGasCost' + +import { expect } from './shared/expect' +import { getMaxTick, getMinTick } from './shared/ticks' +import { computePoolAddress } from './shared/computePoolAddress' + +describe('PairFlash test', () => { + const provider = waffle.provider + const wallets = waffle.provider.getWallets() + const wallet = wallets[0] + + let flash: PairFlash + let nft: MockTimeNonfungiblePositionManager + let token0: TestERC20 + let token1: TestERC20 + let factory: IUniswapV3Factory + let quoter: Quoter + + async function createPool(tokenAddressA: string, tokenAddressB: string, fee: FeeAmount, price: BigNumber) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, fee, price) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: fee, + tickLower: getMinTick(TICK_SPACINGS[fee]), + tickUpper: getMaxTick(TICK_SPACINGS[fee]), + recipient: wallet.address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams) + } + + const flashFixture = async () => { + const { router, tokens, factory, weth9, nft } = await completeFixture(wallets, provider) + const token0 = tokens[0] + const token1 = tokens[1] + + const flashContractFactory = await ethers.getContractFactory('PairFlash') + const flash = (await flashContractFactory.deploy(router.address, factory.address, weth9.address)) as PairFlash + + const quoterFactory = await ethers.getContractFactory('Quoter') + const quoter = (await quoterFactory.deploy(factory.address, weth9.address)) as Quoter + + return { + token0, + token1, + flash, + factory, + weth9, + nft, + quoter, + router, + } + } + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ factory, token0, token1, flash, nft, quoter } = await loadFixture(flashFixture)) + + await token0.approve(nft.address, MaxUint128) + await token1.approve(nft.address, MaxUint128) + await createPool(token0.address, token1.address, FeeAmount.LOW, encodePriceSqrt(5, 10)) + await createPool(token0.address, token1.address, FeeAmount.MEDIUM, encodePriceSqrt(1, 1)) + await createPool(token0.address, token1.address, FeeAmount.HIGH, encodePriceSqrt(20, 10)) + }) + + describe('flash', () => { + it('test correct transfer events', async () => { + //choose amountIn to test + const amount0In = 1000 + const amount1In = 1000 + + const fee0 = Math.ceil((amount0In * FeeAmount.MEDIUM) / 1000000) + const fee1 = Math.ceil((amount1In * FeeAmount.MEDIUM) / 1000000) + + const flashParams = { + token0: token0.address, + token1: token1.address, + fee1: FeeAmount.MEDIUM, + amount0: amount0In, + amount1: amount1In, + fee2: FeeAmount.LOW, + fee3: FeeAmount.HIGH, + } + // pool1 is the borrow pool + const pool1 = computePoolAddress(factory.address, [token0.address, token1.address], FeeAmount.MEDIUM) + const pool2 = computePoolAddress(factory.address, [token0.address, token1.address], FeeAmount.LOW) + const pool3 = computePoolAddress(factory.address, [token0.address, token1.address], FeeAmount.HIGH) + + const expectedAmountOut0 = await quoter.callStatic.quoteExactInputSingle( + token1.address, + token0.address, + FeeAmount.LOW, + amount1In, + encodePriceSqrt(20, 10) + ) + const expectedAmountOut1 = await quoter.callStatic.quoteExactInputSingle( + token0.address, + token1.address, + FeeAmount.HIGH, + amount0In, + encodePriceSqrt(5, 10) + ) + + await expect(flash.initFlash(flashParams)) + .to.emit(token0, 'Transfer') + .withArgs(pool1, flash.address, amount0In) + .to.emit(token1, 'Transfer') + .withArgs(pool1, flash.address, amount1In) + .to.emit(token0, 'Transfer') + .withArgs(pool2, flash.address, expectedAmountOut0) + .to.emit(token1, 'Transfer') + .withArgs(pool3, flash.address, expectedAmountOut1) + .to.emit(token0, 'Transfer') + .withArgs(flash.address, wallet.address, expectedAmountOut0.toNumber() - amount0In - fee0) + .to.emit(token1, 'Transfer') + .withArgs(flash.address, wallet.address, expectedAmountOut1.toNumber() - amount1In - fee1) + }) + + it('gas', async () => { + const amount0In = 1000 + const amount1In = 1000 + + const flashParams = { + token0: token0.address, + token1: token1.address, + fee1: FeeAmount.MEDIUM, + amount0: amount0In, + amount1: amount1In, + fee2: FeeAmount.LOW, + fee3: FeeAmount.HIGH, + } + await snapshotGasCost(flash.initFlash(flashParams)) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/Path.spec.ts b/lib/uniswap/v3-periphery/test/Path.spec.ts new file mode 100644 index 0000000..ba6dc95 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/Path.spec.ts @@ -0,0 +1,90 @@ +import { waffle, ethers } from 'hardhat' +import { FeeAmount } from './shared/constants' + +import { expect } from './shared/expect' + +import { PathTest } from '../typechain' +import { decodePath, encodePath } from './shared/path' + +import snapshotGasCost from './shared/snapshotGasCost' + +describe('Path', () => { + let path: PathTest + + let tokenAddresses = [ + '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707', + '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9', + '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9', + ] + let fees = [FeeAmount.MEDIUM, FeeAmount.MEDIUM] + + const pathTestFixture = async () => { + const pathTestFactory = await ethers.getContractFactory('PathTest') + return (await pathTestFactory.deploy()) as PathTest + } + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + loadFixture = waffle.createFixtureLoader(await (ethers as any).getSigners()) + }) + + beforeEach('deploy PathTest', async () => { + path = await loadFixture(pathTestFixture) + }) + + it('js encoding works as expected', async () => { + let expectedPath = + '0x' + + tokenAddresses + .slice(0, 2) + .map((tokenAddress) => tokenAddress.slice(2).toLowerCase()) + .join('000bb8') + expect(encodePath(tokenAddresses.slice(0, 2), fees.slice(0, 1))).to.eq(expectedPath) + + expectedPath = '0x' + tokenAddresses.map((tokenAddress) => tokenAddress.slice(2).toLowerCase()).join('000bb8') + expect(encodePath(tokenAddresses, fees)).to.eq(expectedPath) + }) + + it('js decoding works as expected', async () => { + const encodedPath = encodePath(tokenAddresses, fees) + const [decodedTokens, decodedFees] = decodePath(encodedPath) + expect(decodedTokens).to.deep.eq(tokenAddresses) + expect(decodedFees).to.deep.eq(fees) + }) + + describe('#hasMultiplePools / #decodeFirstPool / #skipToken / #getFirstPool', () => { + const encodedPath = encodePath(tokenAddresses, fees) + + it('works on first pool', async () => { + expect(await path.hasMultiplePools(encodedPath)).to.be.true + + const firstPool = await path.decodeFirstPool(encodedPath) + expect(firstPool.tokenA).to.be.eq(tokenAddresses[0]) + expect(firstPool.tokenB).to.be.eq(tokenAddresses[1]) + expect(firstPool.fee).to.be.eq(FeeAmount.MEDIUM) + + expect(await path.decodeFirstPool(await path.getFirstPool(encodedPath))).to.deep.eq(firstPool) + }) + + const offset = 20 + 3 + + it('skips 1 item', async () => { + const skipped = await path.skipToken(encodedPath) + expect(skipped).to.be.eq('0x' + encodedPath.slice(2 + offset * 2)) + + expect(await path.hasMultiplePools(skipped)).to.be.false + + const { tokenA, tokenB, fee: decodedFee } = await path.decodeFirstPool(skipped) + expect(tokenA).to.be.eq(tokenAddresses[1]) + expect(tokenB).to.be.eq(tokenAddresses[2]) + expect(decodedFee).to.be.eq(FeeAmount.MEDIUM) + }) + }) + + it('gas cost', async () => { + await snapshotGasCost( + path.getGasCostOfDecodeFirstPool(encodePath([tokenAddresses[0], tokenAddresses[1]], [FeeAmount.MEDIUM])) + ) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/PeripheryImmutableState.spec.ts b/lib/uniswap/v3-periphery/test/PeripheryImmutableState.spec.ts new file mode 100644 index 0000000..f077e3a --- /dev/null +++ b/lib/uniswap/v3-periphery/test/PeripheryImmutableState.spec.ts @@ -0,0 +1,56 @@ +import { Contract } from 'ethers' +import { waffle, ethers } from 'hardhat' + +import { Fixture } from 'ethereum-waffle' +import { PeripheryImmutableStateTest, IWETH9 } from '../typechain' +import { expect } from './shared/expect' +import { v3RouterFixture } from './shared/externalFixtures' + +describe('PeripheryImmutableState', () => { + const nonfungiblePositionManagerFixture: Fixture<{ + weth9: IWETH9 + factory: Contract + state: PeripheryImmutableStateTest + }> = async (wallets, provider) => { + const { weth9, factory } = await v3RouterFixture(wallets, provider) + + const stateFactory = await ethers.getContractFactory('PeripheryImmutableStateTest') + const state = (await stateFactory.deploy(factory.address, weth9.address)) as PeripheryImmutableStateTest + + return { + weth9, + factory, + state, + } + } + + let factory: Contract + let weth9: IWETH9 + let state: PeripheryImmutableStateTest + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + loadFixture = waffle.createFixtureLoader(await (ethers as any).getSigners()) + }) + + beforeEach('load fixture', async () => { + ;({ state, weth9, factory } = await loadFixture(nonfungiblePositionManagerFixture)) + }) + + it('bytecode size', async () => { + expect(((await state.provider.getCode(state.address)).length - 2) / 2).to.matchSnapshot() + }) + + describe('#WETH9', () => { + it('points to WETH9', async () => { + expect(await state.WETH9()).to.eq(weth9.address) + }) + }) + + describe('#factory', () => { + it('points to v3 core factory', async () => { + expect(await state.factory()).to.eq(factory.address) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/PoolAddress.spec.ts b/lib/uniswap/v3-periphery/test/PoolAddress.spec.ts new file mode 100644 index 0000000..9c83481 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/PoolAddress.spec.ts @@ -0,0 +1,72 @@ +import { constants } from 'ethers' +import { waffle, ethers } from 'hardhat' + +import { PoolAddressTest } from '../typechain' +import { POOL_BYTECODE_HASH } from './shared/computePoolAddress' +import { expect } from './shared/expect' +import snapshotGasCost from './shared/snapshotGasCost' + +describe('PoolAddress', () => { + let poolAddress: PoolAddressTest + + const poolAddressTestFixture = async () => { + const poolAddressTestFactory = await ethers.getContractFactory('PoolAddressTest') + return (await poolAddressTestFactory.deploy()) as PoolAddressTest + } + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + loadFixture = waffle.createFixtureLoader(await (ethers as any).getSigners()) + }) + + beforeEach('deploy PoolAddressTest', async () => { + poolAddress = await loadFixture(poolAddressTestFixture) + }) + + describe('#POOL_INIT_CODE_HASH', () => { + it('equals the hash of the pool bytecode', async () => { + expect(await poolAddress.POOL_INIT_CODE_HASH()).to.eq(POOL_BYTECODE_HASH) + }) + }) + + describe('#computeAddress', () => { + it('all arguments equal zero', async () => { + await expect(poolAddress.computeAddress(constants.AddressZero, constants.AddressZero, constants.AddressZero, 0)) + .to.be.reverted + }) + + it('matches example from core repo', async () => { + expect( + await poolAddress.computeAddress( + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x1000000000000000000000000000000000000000', + '0x2000000000000000000000000000000000000000', + 250 + ) + ).to.matchSnapshot() + }) + + it('token argument order cannot be in reverse', async () => { + await expect( + poolAddress.computeAddress( + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x2000000000000000000000000000000000000000', + '0x1000000000000000000000000000000000000000', + 3000 + ) + ).to.be.reverted + }) + + it('gas cost', async () => { + await snapshotGasCost( + poolAddress.getGasCostOfComputeAddress( + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x1000000000000000000000000000000000000000', + '0x2000000000000000000000000000000000000000', + 3000 + ) + ) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/PoolTicksCounter.spec.ts b/lib/uniswap/v3-periphery/test/PoolTicksCounter.spec.ts new file mode 100644 index 0000000..5be0ce0 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/PoolTicksCounter.spec.ts @@ -0,0 +1,280 @@ +import { waffle, ethers, artifacts } from 'hardhat' + +import { expect } from './shared/expect' + +import { PoolTicksCounterTest } from '../typechain' +import { deployMockContract, Fixture, MockContract } from 'ethereum-waffle' +import { Artifact } from 'hardhat/types' + +describe('PoolTicksCounter', () => { + const TICK_SPACINGS = [200, 60, 10] + + TICK_SPACINGS.forEach((TICK_SPACING) => { + let PoolTicksCounter: PoolTicksCounterTest + let pool: MockContract + let PoolAbi: Artifact + + // Bit index to tick + const bitIdxToTick = (idx: number, page = 0) => { + return idx * TICK_SPACING + page * 256 * TICK_SPACING + } + + before(async () => { + const wallets = await (ethers as any).getSigners() + PoolAbi = await artifacts.readArtifact('IUniswapV3Pool') + const poolTicksHelperFactory = await ethers.getContractFactory('PoolTicksCounterTest') + PoolTicksCounter = (await poolTicksHelperFactory.deploy()) as PoolTicksCounterTest + pool = await deployMockContract(wallets[0], PoolAbi.abi) + await pool.mock.tickSpacing.returns(TICK_SPACING) + }) + + describe(`[Tick Spacing: ${TICK_SPACING}]: tick after is bigger`, async () => { + it('same tick initialized', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1100) // 1100 + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(2) + ) + expect(result).to.be.eq(1) + }) + + it('same tick not-initialized', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1100) // 1100 + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(1), + bitIdxToTick(1) + ) + expect(result).to.be.eq(0) + }) + + it('same page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1100) // 1100 + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(0), + bitIdxToTick(255) + ) + expect(result).to.be.eq(2) + }) + + it('multiple pages', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1100) // 1100 + await pool.mock.tickBitmap.withArgs(1).returns(0b1101) // 1101 + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(0), + bitIdxToTick(255, 1) + ) + expect(result).to.be.eq(5) + }) + + it('counts all ticks in a page except ending tick', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(ethers.constants.MaxUint256) + await pool.mock.tickBitmap.withArgs(1).returns(0x0) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(0), + bitIdxToTick(255, 1) + ) + expect(result).to.be.eq(255) + }) + + it('counts ticks to left of start and right of end on same page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1111000100001111) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(8), + bitIdxToTick(255) + ) + expect(result).to.be.eq(4) + }) + + it('counts ticks to left of start and right of end across on multiple pages', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1111000100001111) + await pool.mock.tickBitmap.withArgs(1).returns(0b1111000100001111) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(8), + bitIdxToTick(8, 1) + ) + expect(result).to.be.eq(9) + }) + + it('counts ticks when before and after are initialized on same page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b11111100) + const startingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(255) + ) + expect(startingTickInit).to.be.eq(5) + const endingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(0), + bitIdxToTick(3) + ) + expect(endingTickInit).to.be.eq(2) + const bothInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(5) + ) + expect(bothInit).to.be.eq(3) + }) + + it('counts ticks when before and after are initialized on multiple page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b11111100) + await pool.mock.tickBitmap.withArgs(1).returns(0b11111100) + const startingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(255) + ) + expect(startingTickInit).to.be.eq(5) + const endingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(0), + bitIdxToTick(3, 1) + ) + expect(endingTickInit).to.be.eq(8) + const bothInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(5, 1) + ) + expect(bothInit).to.be.eq(9) + }) + + it('counts ticks with lots of pages', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b11111100) + await pool.mock.tickBitmap.withArgs(1).returns(0b11111111) + await pool.mock.tickBitmap.withArgs(2).returns(0x0) + await pool.mock.tickBitmap.withArgs(3).returns(0x0) + await pool.mock.tickBitmap.withArgs(4).returns(0b11111100) + + const bothInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(4), + bitIdxToTick(5, 4) + ) + expect(bothInit).to.be.eq(15) + }) + }) + + describe(`[Tick Spacing: ${TICK_SPACING}]: tick after is smaller`, async () => { + it('same page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1100) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(255), + bitIdxToTick(0) + ) + expect(result).to.be.eq(2) + }) + + it('multiple pages', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1100) + await pool.mock.tickBitmap.withArgs(-1).returns(0b1100) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(255), + bitIdxToTick(0, -1) + ) + expect(result).to.be.eq(4) + }) + + it('counts all ticks in a page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(ethers.constants.MaxUint256) + await pool.mock.tickBitmap.withArgs(-1).returns(0x0) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(255), + bitIdxToTick(0, -1) + ) + expect(result).to.be.eq(256) + }) + + it('counts ticks to right of start and left of end on same page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1111000100001111) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(15), + bitIdxToTick(2) + ) + expect(result).to.be.eq(6) + }) + + it('counts ticks to right of start and left of end on multiple pages', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b1111000100001111) + await pool.mock.tickBitmap.withArgs(-1).returns(0b1111000100001111) + const result = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(8), + bitIdxToTick(8, -1) + ) + expect(result).to.be.eq(9) + }) + + it('counts ticks when before and after are initialized on same page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b11111100) + const startingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(3), + bitIdxToTick(0) + ) + expect(startingTickInit).to.be.eq(2) + const endingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(255), + bitIdxToTick(2) + ) + expect(endingTickInit).to.be.eq(5) + const bothInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(5), + bitIdxToTick(2) + ) + expect(bothInit).to.be.eq(3) + }) + + it('counts ticks when before and after are initialized on multiple page', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b11111100) + await pool.mock.tickBitmap.withArgs(-1).returns(0b11111100) + const startingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(3, -1) + ) + expect(startingTickInit).to.be.eq(5) + const endingTickInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(5), + bitIdxToTick(255, -1) + ) + expect(endingTickInit).to.be.eq(4) + const bothInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(2), + bitIdxToTick(5, -1) + ) + expect(bothInit).to.be.eq(3) + }) + + it('counts ticks with lots of pages', async () => { + await pool.mock.tickBitmap.withArgs(0).returns(0b11111100) + await pool.mock.tickBitmap.withArgs(-1).returns(0xff) + await pool.mock.tickBitmap.withArgs(-2).returns(0x0) + await pool.mock.tickBitmap.withArgs(-3).returns(0x0) + await pool.mock.tickBitmap.withArgs(-4).returns(0b11111100) + const bothInit = await PoolTicksCounter.countInitializedTicksCrossed( + pool.address, + bitIdxToTick(3), + bitIdxToTick(6, -4) + ) + expect(bothInit).to.be.eq(11) + }) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/PositionValue.spec.ts b/lib/uniswap/v3-periphery/test/PositionValue.spec.ts new file mode 100644 index 0000000..6bb7ef4 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/PositionValue.spec.ts @@ -0,0 +1,490 @@ +import { waffle, ethers } from 'hardhat' +import { constants, BigNumberish, Contract } from 'ethers' +import { Fixture } from 'ethereum-waffle' +import { + PositionValueTest, + SwapRouter, + MockTimeNonfungiblePositionManager, + IUniswapV3Pool, + TestERC20, + IUniswapV3Factory, +} from '../typechain' +import { FeeAmount, MaxUint128, TICK_SPACINGS } from './shared/constants' +import { getMaxTick, getMinTick } from './shared/ticks' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import { encodePath } from './shared/path' +import { computePoolAddress } from './shared/computePoolAddress' +import completeFixture from './shared/completeFixture' +import snapshotGasCost from './shared/snapshotGasCost' + +import { expect } from './shared/expect' + +import { abi as IUniswapV3PoolABI } from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json' + +describe('PositionValue', async () => { + const [...wallets] = waffle.provider.getWallets() + const positionValueCompleteFixture: Fixture<{ + positionValue: PositionValueTest + tokens: [TestERC20, TestERC20, TestERC20] + nft: MockTimeNonfungiblePositionManager + router: SwapRouter + factory: IUniswapV3Factory + }> = async (wallets, provider) => { + const { nft, router, tokens, factory } = await completeFixture(wallets, provider) + const positionValueFactory = await ethers.getContractFactory('PositionValueTest') + const positionValue = (await positionValueFactory.deploy()) as PositionValueTest + + for (const token of tokens) { + await token.approve(nft.address, constants.MaxUint256) + await token.connect(wallets[0]).approve(nft.address, constants.MaxUint256) + await token.transfer(wallets[0].address, expandTo18Decimals(1_000_000)) + } + + return { + positionValue, + tokens, + nft, + router, + factory, + } + } + + let pool: Contract + let tokens: [TestERC20, TestERC20, TestERC20] + let positionValue: PositionValueTest + let nft: MockTimeNonfungiblePositionManager + let router: SwapRouter + let factory: IUniswapV3Factory + + let amountDesired: BigNumberish + + let loadFixture: ReturnType + before('create fixture loader', async () => { + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach(async () => { + ;({ positionValue, tokens, nft, router, factory } = await loadFixture(positionValueCompleteFixture)) + await nft.createAndInitializePoolIfNecessary( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + const poolAddress = computePoolAddress(factory.address, [tokens[0].address, tokens[1].address], FeeAmount.MEDIUM) + pool = new ethers.Contract(poolAddress, IUniswapV3PoolABI, wallets[0]) + }) + + describe('#total', () => { + let tokenId: number + let sqrtRatioX96: BigNumberish + + beforeEach(async () => { + amountDesired = expandTo18Decimals(100_000) + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const swapAmount = expandTo18Decimals(1_000) + await tokens[0].approve(router.address, swapAmount) + await tokens[1].approve(router.address, swapAmount) + + // accmuluate token0 fees + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + amountIn: swapAmount, + amountOutMinimum: 0, + }) + + // accmuluate token1 fees + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + amountIn: swapAmount, + amountOutMinimum: 0, + }) + + sqrtRatioX96 = (await pool.slot0()).sqrtPriceX96 + }) + + it('returns the correct amount', async () => { + const principal = await positionValue.principal(nft.address, 1, sqrtRatioX96) + const fees = await positionValue.fees(nft.address, 1) + const total = await positionValue.total(nft.address, 1, sqrtRatioX96) + + expect(total[0]).to.equal(principal[0].add(fees[0])) + expect(total[1]).to.equal(principal[1].add(fees[1])) + }) + + it('gas', async () => { + await snapshotGasCost(positionValue.totalGas(nft.address, 1, sqrtRatioX96)) + }) + }) + + describe('#principal', () => { + let sqrtRatioX96: BigNumberish + + beforeEach(async () => { + amountDesired = expandTo18Decimals(100_000) + sqrtRatioX96 = (await pool.slot0()).sqrtPriceX96 + }) + + it('returns the correct values when price is in the middle of the range', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const principal = await positionValue.principal(nft.address, 1, sqrtRatioX96) + expect(principal.amount0).to.equal('99999999999999999999999') + expect(principal.amount1).to.equal('99999999999999999999999') + }) + + it('returns the correct values when range is below current price', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: -60, + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const principal = await positionValue.principal(nft.address, 1, sqrtRatioX96) + expect(principal.amount0).to.equal('0') + expect(principal.amount1).to.equal('99999999999999999999999') + }) + + it('returns the correct values when range is below current price', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: 60, + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const principal = await positionValue.principal(nft.address, 1, sqrtRatioX96) + expect(principal.amount0).to.equal('99999999999999999999999') + expect(principal.amount1).to.equal('0') + }) + + it('returns the correct values when range is skewed above price', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: -6_000, + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const principal = await positionValue.principal(nft.address, 1, sqrtRatioX96) + expect(principal.amount0).to.equal('99999999999999999999999') + expect(principal.amount1).to.equal('25917066770240321655335') + }) + + it('returns the correct values when range is skewed below price', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: 6_000, + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const principal = await positionValue.principal(nft.address, 1, sqrtRatioX96) + expect(principal.amount0).to.equal('25917066770240321655335') + expect(principal.amount1).to.equal('99999999999999999999999') + }) + + it('gas', async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + await snapshotGasCost(positionValue.principalGas(nft.address, 1, sqrtRatioX96)) + }) + }) + + describe('#fees', () => { + let tokenId: number + + beforeEach(async () => { + amountDesired = expandTo18Decimals(100_000) + tokenId = 2 + + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + }) + + describe('when price is within the position range', () => { + beforeEach(async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: TICK_SPACINGS[FeeAmount.MEDIUM] * -1_000, + tickUpper: TICK_SPACINGS[FeeAmount.MEDIUM] * 1_000, + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: amountDesired, + amount1Desired: amountDesired, + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + const swapAmount = expandTo18Decimals(1_000) + await tokens[0].approve(router.address, swapAmount) + await tokens[1].approve(router.address, swapAmount) + + // accmuluate token0 fees + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + amountIn: swapAmount, + amountOutMinimum: 0, + }) + + // accmuluate token1 fees + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + amountIn: swapAmount, + amountOutMinimum: 0, + }) + }) + + it('return the correct amount of fees', async () => { + const feesFromCollect = await nft.callStatic.collect({ + tokenId, + recipient: wallets[0].address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + const feeAmounts = await positionValue.fees(nft.address, tokenId) + + expect(feeAmounts[0]).to.equal(feesFromCollect[0]) + expect(feeAmounts[1]).to.equal(feesFromCollect[1]) + }) + + it('returns the correct amount of fees if tokensOwed fields are greater than 0', async () => { + await nft.increaseLiquidity({ + tokenId: tokenId, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + }) + + const swapAmount = expandTo18Decimals(1_000) + await tokens[0].approve(router.address, swapAmount) + + // accmuluate more token0 fees after clearing initial amount + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + amountIn: swapAmount, + amountOutMinimum: 0, + }) + + const feesFromCollect = await nft.callStatic.collect({ + tokenId, + recipient: wallets[0].address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + const feeAmounts = await positionValue.fees(nft.address, tokenId) + expect(feeAmounts[0]).to.equal(feesFromCollect[0]) + expect(feeAmounts[1]).to.equal(feesFromCollect[1]) + }) + + it('gas', async () => { + await snapshotGasCost(positionValue.feesGas(nft.address, tokenId)) + }) + }) + + describe('when price is below the position range', async () => { + beforeEach(async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: TICK_SPACINGS[FeeAmount.MEDIUM] * -10, + tickUpper: TICK_SPACINGS[FeeAmount.MEDIUM] * 10, + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: expandTo18Decimals(10_000), + amount1Desired: expandTo18Decimals(10_000), + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + await tokens[0].approve(router.address, constants.MaxUint256) + await tokens[1].approve(router.address, constants.MaxUint256) + + // accumulate token1 fees + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + amountIn: expandTo18Decimals(1_000), + amountOutMinimum: 0, + }) + + // accumulate token0 fees and push price below tickLower + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + amountIn: expandTo18Decimals(50_000), + amountOutMinimum: 0, + }) + }) + + it('returns the correct amount of fees', async () => { + const feesFromCollect = await nft.callStatic.collect({ + tokenId, + recipient: wallets[0].address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + + const feeAmounts = await positionValue.fees(nft.address, tokenId) + expect(feeAmounts[0]).to.equal(feesFromCollect[0]) + expect(feeAmounts[1]).to.equal(feesFromCollect[1]) + }) + + it('gas', async () => { + await snapshotGasCost(positionValue.feesGas(nft.address, tokenId)) + }) + }) + + describe('when price is above the position range', async () => { + beforeEach(async () => { + await nft.mint({ + token0: tokens[0].address, + token1: tokens[1].address, + tickLower: TICK_SPACINGS[FeeAmount.MEDIUM] * -10, + tickUpper: TICK_SPACINGS[FeeAmount.MEDIUM] * 10, + fee: FeeAmount.MEDIUM, + recipient: wallets[0].address, + amount0Desired: expandTo18Decimals(10_000), + amount1Desired: expandTo18Decimals(10_000), + amount0Min: 0, + amount1Min: 0, + deadline: 10, + }) + + await tokens[0].approve(router.address, constants.MaxUint256) + await tokens[1].approve(router.address, constants.MaxUint256) + + // accumulate token0 fees + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + amountIn: expandTo18Decimals(1_000), + amountOutMinimum: 0, + }) + + // accumulate token1 fees and push price above tickUpper + await router.exactInput({ + recipient: wallets[0].address, + deadline: 1, + path: encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + amountIn: expandTo18Decimals(50_000), + amountOutMinimum: 0, + }) + }) + + it('returns the correct amount of fees', async () => { + const feesFromCollect = await nft.callStatic.collect({ + tokenId, + recipient: wallets[0].address, + amount0Max: MaxUint128, + amount1Max: MaxUint128, + }) + const feeAmounts = await positionValue.fees(nft.address, tokenId) + expect(feeAmounts[0]).to.equal(feesFromCollect[0]) + expect(feeAmounts[1]).to.equal(feesFromCollect[1]) + }) + + it('gas', async () => { + await snapshotGasCost(positionValue.feesGas(nft.address, tokenId)) + }) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/Quoter.spec.ts b/lib/uniswap/v3-periphery/test/Quoter.spec.ts new file mode 100644 index 0000000..6236a1f --- /dev/null +++ b/lib/uniswap/v3-periphery/test/Quoter.spec.ts @@ -0,0 +1,201 @@ +import { Fixture } from 'ethereum-waffle' +import { constants, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { MockTimeNonfungiblePositionManager, Quoter, TestERC20 } from '../typechain' +import completeFixture from './shared/completeFixture' +import { FeeAmount, MaxUint128, TICK_SPACINGS } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import { expect } from './shared/expect' +import { encodePath } from './shared/path' +import { createPool } from './shared/quoter' + +describe('Quoter', () => { + let wallet: Wallet + let trader: Wallet + + const swapRouterFixture: Fixture<{ + nft: MockTimeNonfungiblePositionManager + tokens: [TestERC20, TestERC20, TestERC20] + quoter: Quoter + }> = async (wallets, provider) => { + const { weth9, factory, router, tokens, nft } = await completeFixture(wallets, provider) + + // approve & fund wallets + for (const token of tokens) { + await token.approve(router.address, constants.MaxUint256) + await token.approve(nft.address, constants.MaxUint256) + await token.connect(trader).approve(router.address, constants.MaxUint256) + await token.transfer(trader.address, expandTo18Decimals(1_000_000)) + } + + const quoterFactory = await ethers.getContractFactory('Quoter') + quoter = (await quoterFactory.deploy(factory.address, weth9.address)) as Quoter + + return { + tokens, + nft, + quoter, + } + } + + let nft: MockTimeNonfungiblePositionManager + let tokens: [TestERC20, TestERC20, TestERC20] + let quoter: Quoter + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + const wallets = await (ethers as any).getSigners() + ;[wallet, trader] = wallets + loadFixture = waffle.createFixtureLoader(wallets) + }) + + // helper for getting weth and token balances + beforeEach('load fixture', async () => { + ;({ tokens, nft, quoter } = await loadFixture(swapRouterFixture)) + }) + + describe('quotes', () => { + beforeEach(async () => { + await createPool(nft, wallet, tokens[0].address, tokens[1].address) + await createPool(nft, wallet, tokens[1].address, tokens[2].address) + }) + + describe('#quoteExactInput', () => { + it('0 -> 1', async () => { + const quote = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + 3 + ) + + expect(quote).to.eq(1) + }) + + it('1 -> 0', async () => { + const quote = await quoter.callStatic.quoteExactInput( + encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + 3 + ) + + expect(quote).to.eq(1) + }) + + it('0 -> 1 -> 2', async () => { + const quote = await quoter.callStatic.quoteExactInput( + encodePath( + tokens.map((token) => token.address), + [FeeAmount.MEDIUM, FeeAmount.MEDIUM] + ), + 5 + ) + + expect(quote).to.eq(1) + }) + + it('2 -> 1 -> 0', async () => { + const quote = await quoter.callStatic.quoteExactInput( + encodePath(tokens.map((token) => token.address).reverse(), [FeeAmount.MEDIUM, FeeAmount.MEDIUM]), + 5 + ) + + expect(quote).to.eq(1) + }) + }) + + describe('#quoteExactInputSingle', () => { + it('0 -> 1', async () => { + const quote = await quoter.callStatic.quoteExactInputSingle( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + MaxUint128, + // -2% + encodePriceSqrt(100, 102) + ) + + expect(quote).to.eq(9852) + }) + + it('1 -> 0', async () => { + const quote = await quoter.callStatic.quoteExactInputSingle( + tokens[1].address, + tokens[0].address, + FeeAmount.MEDIUM, + MaxUint128, + // +2% + encodePriceSqrt(102, 100) + ) + + expect(quote).to.eq(9852) + }) + }) + + describe('#quoteExactOutput', () => { + it('0 -> 1', async () => { + const quote = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + 1 + ) + + expect(quote).to.eq(3) + }) + + it('1 -> 0', async () => { + const quote = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + 1 + ) + + expect(quote).to.eq(3) + }) + + it('0 -> 1 -> 2', async () => { + const quote = await quoter.callStatic.quoteExactOutput( + encodePath(tokens.map((token) => token.address).reverse(), [FeeAmount.MEDIUM, FeeAmount.MEDIUM]), + 1 + ) + + expect(quote).to.eq(5) + }) + + it('2 -> 1 -> 0', async () => { + const quote = await quoter.callStatic.quoteExactOutput( + encodePath( + tokens.map((token) => token.address), + [FeeAmount.MEDIUM, FeeAmount.MEDIUM] + ), + 1 + ) + + expect(quote).to.eq(5) + }) + }) + + describe('#quoteExactOutputSingle', () => { + it('0 -> 1', async () => { + const quote = await quoter.callStatic.quoteExactOutputSingle( + tokens[0].address, + tokens[1].address, + FeeAmount.MEDIUM, + MaxUint128, + encodePriceSqrt(100, 102) + ) + + expect(quote).to.eq(9981) + }) + + it('1 -> 0', async () => { + const quote = await quoter.callStatic.quoteExactOutputSingle( + tokens[1].address, + tokens[0].address, + FeeAmount.MEDIUM, + MaxUint128, + encodePriceSqrt(102, 100) + ) + + expect(quote).to.eq(9981) + }) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/QuoterV2.spec.ts b/lib/uniswap/v3-periphery/test/QuoterV2.spec.ts new file mode 100644 index 0000000..8d1b567 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/QuoterV2.spec.ts @@ -0,0 +1,578 @@ +import { Fixture } from 'ethereum-waffle' +import { constants, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { MockTimeNonfungiblePositionManager, QuoterV2, TestERC20 } from '../typechain' +import completeFixture from './shared/completeFixture' +import { FeeAmount, MaxUint128 } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import { expect } from './shared/expect' +import { encodePath } from './shared/path' +import { createPool, createPoolWithMultiplePositions, createPoolWithZeroTickInitialized } from './shared/quoter' +import snapshotGasCost from './shared/snapshotGasCost' + +describe('QuoterV2', function () { + this.timeout(40000) + let wallet: Wallet + let trader: Wallet + + const swapRouterFixture: Fixture<{ + nft: MockTimeNonfungiblePositionManager + tokens: [TestERC20, TestERC20, TestERC20] + quoter: QuoterV2 + }> = async (wallets, provider) => { + const { weth9, factory, router, tokens, nft } = await completeFixture(wallets, provider) + + // approve & fund wallets + for (const token of tokens) { + await token.approve(router.address, constants.MaxUint256) + await token.approve(nft.address, constants.MaxUint256) + await token.connect(trader).approve(router.address, constants.MaxUint256) + await token.transfer(trader.address, expandTo18Decimals(1_000_000)) + } + + const quoterFactory = await ethers.getContractFactory('QuoterV2') + quoter = (await quoterFactory.deploy(factory.address, weth9.address)) as QuoterV2 + + return { + tokens, + nft, + quoter, + } + } + + let nft: MockTimeNonfungiblePositionManager + let tokens: [TestERC20, TestERC20, TestERC20] + let quoter: QuoterV2 + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + const wallets = await (ethers as any).getSigners() + ;[wallet, trader] = wallets + loadFixture = waffle.createFixtureLoader(wallets) + }) + + // helper for getting weth and token balances + beforeEach('load fixture', async () => { + ;({ tokens, nft, quoter } = await loadFixture(swapRouterFixture)) + }) + + describe('quotes', () => { + beforeEach(async () => { + await createPool(nft, wallet, tokens[0].address, tokens[1].address) + await createPool(nft, wallet, tokens[1].address, tokens[2].address) + await createPoolWithMultiplePositions(nft, wallet, tokens[0].address, tokens[2].address) + }) + + describe('#quoteExactInput', () => { + it('0 -> 2 cross 2 tick', async () => { + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 10000 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('78461846509168490764501028180') + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(amountOut).to.eq(9871) + }) + + it('0 -> 2 cross 2 tick where after is initialized', async () => { + // The swap amount is set such that the active tick after the swap is -120. + // -120 is an initialized tick for this pool. We check that we don't count it. + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 6200 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('78757224507315167622282810783') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(amountOut).to.eq(6143) + }) + + it('0 -> 2 cross 1 tick', async () => { + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 4000 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('78926452400586371254602774705') + expect(amountOut).to.eq(3971) + }) + + it('0 -> 2 cross 0 tick, starting tick not initialized', async () => { + // Tick before 0, tick after -1. + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 10 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79227483487511329217250071027') + expect(amountOut).to.eq(8) + }) + + it('0 -> 2 cross 0 tick, starting tick initialized', async () => { + // Tick before 0, tick after -1. Tick 0 initialized. + await createPoolWithZeroTickInitialized(nft, wallet, tokens[0].address, tokens[2].address) + + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 10 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79227817515327498931091950511') + expect(amountOut).to.eq(8) + }) + + it('2 -> 0 cross 2', async () => { + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 10000 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('80001962924147897865541384515') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(amountOut).to.eq(9871) + }) + + it('2 -> 0 cross 2 where tick after is initialized', async () => { + // The swap amount is set such that the active tick after the swap is 120. + // 120 is an initialized tick for this pool. We check we don't count it. + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 6250 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79705728824507063507279123685') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(amountOut).to.eq(6190) + }) + + it('2 -> 0 cross 0 tick, starting tick initialized', async () => { + // Tick 0 initialized. Tick after = 1 + await createPoolWithZeroTickInitialized(nft, wallet, tokens[0].address, tokens[2].address) + + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 200 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79235729830182478001034429156') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(amountOut).to.eq(198) + }) + + it('2 -> 0 cross 0 tick, starting tick not initialized', async () => { + // Tick 0 initialized. Tick after = 1 + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 103 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79235858216754624215638319723') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(amountOut).to.eq(101) + }) + + it('2 -> 1', async () => { + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[2].address, tokens[1].address], [FeeAmount.MEDIUM]), + 10000 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('80018067294531553039351583520') + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(amountOut).to.eq(9871) + }) + + it('0 -> 2 -> 1', async () => { + const { + amountOut, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactInput( + encodePath([tokens[0].address, tokens[2].address, tokens[1].address], [FeeAmount.MEDIUM, FeeAmount.MEDIUM]), + 10000 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(2) + expect(sqrtPriceX96AfterList[0]).to.eq('78461846509168490764501028180') + expect(sqrtPriceX96AfterList[1]).to.eq('80007846861567212939802016351') + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(initializedTicksCrossedList[1]).to.eq(0) + expect(amountOut).to.eq(9745) + }) + }) + + describe('#quoteExactInputSingle', () => { + it('0 -> 2', async () => { + const { + amountOut: quote, + sqrtPriceX96After, + initializedTicksCrossed, + gasEstimate, + } = await quoter.callStatic.quoteExactInputSingle({ + tokenIn: tokens[0].address, + tokenOut: tokens[2].address, + fee: FeeAmount.MEDIUM, + amountIn: 10000, + // -2% + sqrtPriceLimitX96: encodePriceSqrt(100, 102), + }) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossed).to.eq(2) + expect(quote).to.eq(9871) + expect(sqrtPriceX96After).to.eq('78461846509168490764501028180') + }) + + it('2 -> 0', async () => { + const { + amountOut: quote, + sqrtPriceX96After, + initializedTicksCrossed, + gasEstimate, + } = await quoter.callStatic.quoteExactInputSingle({ + tokenIn: tokens[2].address, + tokenOut: tokens[0].address, + fee: FeeAmount.MEDIUM, + amountIn: 10000, + // +2% + sqrtPriceLimitX96: encodePriceSqrt(102, 100), + }) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossed).to.eq(2) + expect(quote).to.eq(9871) + expect(sqrtPriceX96After).to.eq('80001962924147897865541384515') + }) + }) + + describe('#quoteExactOutput', () => { + it('0 -> 2 cross 2 tick', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 15000 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(amountIn).to.eq(15273) + + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('78055527257643669242286029831') + }) + + it('0 -> 2 cross 2 where tick after is initialized', async () => { + // The swap amount is set such that the active tick after the swap is -120. + // -120 is an initialized tick for this pool. We check that we count it. + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 6143 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('78757225449310403327341205211') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(amountIn).to.eq(6200) + }) + + it('0 -> 2 cross 1 tick', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 4000 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(amountIn).to.eq(4029) + + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('78924219757724709840818372098') + }) + + it('0 -> 2 cross 0 tick starting tick initialized', async () => { + // Tick before 0, tick after 1. Tick 0 initialized. + await createPoolWithZeroTickInitialized(nft, wallet, tokens[0].address, tokens[2].address) + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 100 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(amountIn).to.eq(102) + + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79224329176051641448521403903') + }) + + it('0 -> 2 cross 0 tick starting tick not initialized', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[2].address, tokens[0].address], [FeeAmount.MEDIUM]), + 10 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(amountIn).to.eq(12) + + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79227408033628034983534698435') + }) + + it('2 -> 0 cross 2 ticks', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 15000 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList.length).to.eq(1) + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(amountIn).to.eq(15273) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('80418414376567919517220409857') + }) + + it('2 -> 0 cross 2 where tick after is initialized', async () => { + // The swap amount is set such that the active tick after the swap is 120. + // 120 is an initialized tick for this pool. We check that we don't count it. + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 6223 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(2) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79708304437530892332449657932') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(amountIn).to.eq(6283) + }) + + it('2 -> 0 cross 1 tick', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[0].address, tokens[2].address], [FeeAmount.MEDIUM]), + 6000 + ) + + await snapshotGasCost(gasEstimate) + expect(initializedTicksCrossedList[0]).to.eq(1) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('79690640184021170956740081887') + expect(initializedTicksCrossedList.length).to.eq(1) + expect(amountIn).to.eq(6055) + }) + + it('2 -> 1', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[1].address, tokens[2].address], [FeeAmount.MEDIUM]), + 9871 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(1) + expect(sqrtPriceX96AfterList[0]).to.eq('80018020393569259756601362385') + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(amountIn).to.eq(10000) + }) + + it('0 -> 2 -> 1', async () => { + const { + amountIn, + sqrtPriceX96AfterList, + initializedTicksCrossedList, + gasEstimate, + } = await quoter.callStatic.quoteExactOutput( + encodePath([tokens[0].address, tokens[2].address, tokens[1].address].reverse(), [ + FeeAmount.MEDIUM, + FeeAmount.MEDIUM, + ]), + 9745 + ) + + await snapshotGasCost(gasEstimate) + expect(sqrtPriceX96AfterList.length).to.eq(2) + expect(sqrtPriceX96AfterList[0]).to.eq('80007838904387594703933785072') + expect(sqrtPriceX96AfterList[1]).to.eq('78461888503179331029803316753') + expect(initializedTicksCrossedList[0]).to.eq(0) + expect(initializedTicksCrossedList[1]).to.eq(2) + expect(amountIn).to.eq(10000) + }) + }) + + describe('#quoteExactOutputSingle', () => { + it('0 -> 1', async () => { + const { + amountIn, + sqrtPriceX96After, + initializedTicksCrossed, + gasEstimate, + } = await quoter.callStatic.quoteExactOutputSingle({ + tokenIn: tokens[0].address, + tokenOut: tokens[1].address, + fee: FeeAmount.MEDIUM, + amount: MaxUint128, + sqrtPriceLimitX96: encodePriceSqrt(100, 102), + }) + + await snapshotGasCost(gasEstimate) + expect(amountIn).to.eq(9981) + expect(initializedTicksCrossed).to.eq(0) + expect(sqrtPriceX96After).to.eq('78447570448055484695608110440') + }) + + it('1 -> 0', async () => { + const { + amountIn, + sqrtPriceX96After, + initializedTicksCrossed, + gasEstimate, + } = await quoter.callStatic.quoteExactOutputSingle({ + tokenIn: tokens[1].address, + tokenOut: tokens[0].address, + fee: FeeAmount.MEDIUM, + amount: MaxUint128, + sqrtPriceLimitX96: encodePriceSqrt(102, 100), + }) + + await snapshotGasCost(gasEstimate) + expect(amountIn).to.eq(9981) + expect(initializedTicksCrossed).to.eq(0) + expect(sqrtPriceX96After).to.eq('80016521857016594389520272648') + }) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/SelfPermit.spec.ts b/lib/uniswap/v3-periphery/test/SelfPermit.spec.ts new file mode 100644 index 0000000..a76c505 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/SelfPermit.spec.ts @@ -0,0 +1,198 @@ +import { constants, Wallet } from 'ethers' +import { waffle, ethers } from 'hardhat' + +import { Fixture } from 'ethereum-waffle' +import { SelfPermitTest, TestERC20PermitAllowed } from '../typechain' +import { expect } from 'chai' +import { getPermitSignature } from './shared/permit' + +describe('SelfPermit', () => { + let wallet: Wallet + let other: Wallet + + const fixture: Fixture<{ + token: TestERC20PermitAllowed + selfPermitTest: SelfPermitTest + }> = async (wallets, provider) => { + const tokenFactory = await ethers.getContractFactory('TestERC20PermitAllowed') + const token = (await tokenFactory.deploy(0)) as TestERC20PermitAllowed + + const selfPermitTestFactory = await ethers.getContractFactory('SelfPermitTest') + const selfPermitTest = (await selfPermitTestFactory.deploy()) as SelfPermitTest + + return { + token, + selfPermitTest, + } + } + + let token: TestERC20PermitAllowed + let selfPermitTest: SelfPermitTest + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + const wallets = await (ethers as any).getSigners() + ;[wallet, other] = wallets + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ token, selfPermitTest } = await loadFixture(fixture)) + }) + + it('#permit', async () => { + const value = 123 + + const { v, r, s } = await getPermitSignature(wallet, token, other.address, value) + + expect(await token.allowance(wallet.address, other.address)).to.be.eq(0) + await token['permit(address,address,uint256,uint256,uint8,bytes32,bytes32)']( + wallet.address, + other.address, + value, + constants.MaxUint256, + v, + r, + s + ) + expect(await token.allowance(wallet.address, other.address)).to.be.eq(value) + }) + + describe('#selfPermit', () => { + const value = 456 + + it('works', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, value) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await selfPermitTest.selfPermit(token.address, value, constants.MaxUint256, v, r, s) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(value) + }) + + it('fails if permit is submitted externally', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, value) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await token['permit(address,address,uint256,uint256,uint8,bytes32,bytes32)']( + wallet.address, + selfPermitTest.address, + value, + constants.MaxUint256, + v, + r, + s + ) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(value) + + await expect(selfPermitTest.selfPermit(token.address, value, constants.MaxUint256, v, r, s)).to.be.revertedWith( + 'ERC20Permit: invalid signature' + ) + }) + }) + + describe('#selfPermitIfNecessary', () => { + const value = 789 + + it('works', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, value) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await selfPermitTest.selfPermitIfNecessary(token.address, value, constants.MaxUint256, v, r, s) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(value) + }) + + it('does not fail if permit is submitted externally', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, value) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await token['permit(address,address,uint256,uint256,uint8,bytes32,bytes32)']( + wallet.address, + selfPermitTest.address, + value, + constants.MaxUint256, + v, + r, + s + ) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(value) + + await selfPermitTest.selfPermitIfNecessary(token.address, value, constants.MaxUint256, v, r, s) + }) + }) + + describe('#selfPermitAllowed', () => { + it('works', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, constants.MaxUint256) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await expect(selfPermitTest.selfPermitAllowed(token.address, 0, constants.MaxUint256, v, r, s)) + .to.emit(token, 'Approval') + .withArgs(wallet.address, selfPermitTest.address, constants.MaxUint256) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(constants.MaxUint256) + }) + + it('fails if permit is submitted externally', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, constants.MaxUint256) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await token['permit(address,address,uint256,uint256,bool,uint8,bytes32,bytes32)']( + wallet.address, + selfPermitTest.address, + 0, + constants.MaxUint256, + true, + v, + r, + s + ) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(constants.MaxUint256) + + await expect( + selfPermitTest.selfPermitAllowed(token.address, 0, constants.MaxUint256, v, r, s) + ).to.be.revertedWith('TestERC20PermitAllowed::permit: wrong nonce') + }) + }) + + describe('#selfPermitAllowedIfNecessary', () => { + it('works', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, constants.MaxUint256) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.eq(0) + await expect(selfPermitTest.selfPermitAllowedIfNecessary(token.address, 0, constants.MaxUint256, v, r, s)) + .to.emit(token, 'Approval') + .withArgs(wallet.address, selfPermitTest.address, constants.MaxUint256) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.eq(constants.MaxUint256) + }) + + it('skips if already max approved', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, constants.MaxUint256) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await token.approve(selfPermitTest.address, constants.MaxUint256) + await expect( + selfPermitTest.selfPermitAllowedIfNecessary(token.address, 0, constants.MaxUint256, v, r, s) + ).to.not.emit(token, 'Approval') + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.eq(constants.MaxUint256) + }) + + it('does not fail if permit is submitted externally', async () => { + const { v, r, s } = await getPermitSignature(wallet, token, selfPermitTest.address, constants.MaxUint256) + + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(0) + await token['permit(address,address,uint256,uint256,bool,uint8,bytes32,bytes32)']( + wallet.address, + selfPermitTest.address, + 0, + constants.MaxUint256, + true, + v, + r, + s + ) + expect(await token.allowance(wallet.address, selfPermitTest.address)).to.be.eq(constants.MaxUint256) + + await selfPermitTest.selfPermitAllowedIfNecessary(token.address, 0, constants.MaxUint256, v, r, s) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/SwapRouter.gas.spec.ts b/lib/uniswap/v3-periphery/test/SwapRouter.gas.spec.ts new file mode 100644 index 0000000..fb4a0d0 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/SwapRouter.gas.spec.ts @@ -0,0 +1,453 @@ +import { abi as IUniswapV3PoolABI } from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json' +import { Fixture } from 'ethereum-waffle' +import { BigNumber, constants, ContractTransaction, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { IUniswapV3Pool, IWETH9, MockTimeSwapRouter, TestERC20 } from '../typechain' +import completeFixture from './shared/completeFixture' +import { FeeAmount, TICK_SPACINGS } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import { expect } from './shared/expect' +import { encodePath } from './shared/path' +import snapshotGasCost from './shared/snapshotGasCost' +import { getMaxTick, getMinTick } from './shared/ticks' + +describe('SwapRouter gas tests', function () { + this.timeout(40000) + let wallet: Wallet + let trader: Wallet + + const swapRouterFixture: Fixture<{ + weth9: IWETH9 + router: MockTimeSwapRouter + tokens: [TestERC20, TestERC20, TestERC20] + pools: [IUniswapV3Pool, IUniswapV3Pool, IUniswapV3Pool] + }> = async (wallets, provider) => { + const { weth9, factory, router, tokens, nft } = await completeFixture(wallets, provider) + + // approve & fund wallets + for (const token of tokens) { + await token.approve(router.address, constants.MaxUint256) + await token.approve(nft.address, constants.MaxUint256) + await token.connect(trader).approve(router.address, constants.MaxUint256) + await token.transfer(trader.address, expandTo18Decimals(1_000_000)) + } + + const liquidity = 1000000 + async function createPool(tokenAddressA: string, tokenAddressB: string) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary( + tokenAddressA, + tokenAddressB, + FeeAmount.MEDIUM, + encodePriceSqrt(100005, 100000) // we don't want to cross any ticks + ) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallet.address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams) + } + + async function createPoolWETH9(tokenAddress: string) { + await weth9.deposit({ value: liquidity * 2 }) + await weth9.approve(nft.address, constants.MaxUint256) + return createPool(weth9.address, tokenAddress) + } + + // create pools + await createPool(tokens[0].address, tokens[1].address) + await createPool(tokens[1].address, tokens[2].address) + await createPoolWETH9(tokens[0].address) + + const poolAddresses = await Promise.all([ + factory.getPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM), + factory.getPool(tokens[1].address, tokens[2].address, FeeAmount.MEDIUM), + factory.getPool(weth9.address, tokens[0].address, FeeAmount.MEDIUM), + ]) + + const pools = poolAddresses.map((poolAddress) => new ethers.Contract(poolAddress, IUniswapV3PoolABI, wallet)) as [ + IUniswapV3Pool, + IUniswapV3Pool, + IUniswapV3Pool + ] + + return { + weth9, + router, + tokens, + pools, + } + } + + let weth9: IWETH9 + let router: MockTimeSwapRouter + let tokens: [TestERC20, TestERC20, TestERC20] + let pools: [IUniswapV3Pool, IUniswapV3Pool, IUniswapV3Pool] + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + const wallets = await (ethers as any).getSigners() + ;[wallet, trader] = wallets + + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ router, weth9, tokens, pools } = await loadFixture(swapRouterFixture)) + }) + + async function exactInput( + tokens: string[], + amountIn: number = 2, + amountOutMinimum: number = 1 + ): Promise { + const inputIsWETH = weth9.address === tokens[0] + const outputIsWETH9 = tokens[tokens.length - 1] === weth9.address + + const value = inputIsWETH ? amountIn : 0 + + const params = { + path: encodePath(tokens, new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)), + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountIn, + amountOutMinimum: outputIsWETH9 ? 0 : amountOutMinimum, // save on calldata, + } + + const data = [router.interface.encodeFunctionData('exactInput', [params])] + if (outputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOutMinimum, trader.address])) + + // optimized for the gas test + return data.length === 1 + ? router.connect(trader).exactInput(params, { value }) + : router.connect(trader).multicall(data, { value }) + } + + async function exactInputSingle( + tokenIn: string, + tokenOut: string, + amountIn: number = 3, + amountOutMinimum: number = 1, + sqrtPriceLimitX96?: BigNumber + ): Promise { + const inputIsWETH = weth9.address === tokenIn + const outputIsWETH9 = tokenOut === weth9.address + + const value = inputIsWETH ? amountIn : 0 + + const params = { + tokenIn, + tokenOut, + fee: FeeAmount.MEDIUM, + sqrtPriceLimitX96: sqrtPriceLimitX96 ?? 0, + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountIn, + amountOutMinimum: outputIsWETH9 ? 0 : amountOutMinimum, // save on calldata + } + + const data = [router.interface.encodeFunctionData('exactInputSingle', [params])] + if (outputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOutMinimum, trader.address])) + + // optimized for the gas test + return data.length === 1 + ? router.connect(trader).exactInputSingle(params, { value }) + : router.connect(trader).multicall(data, { value }) + } + + async function exactOutput(tokens: string[]): Promise { + const amountInMaximum = 10 // we don't care + const amountOut = 1 + + const inputIsWETH9 = tokens[0] === weth9.address + const outputIsWETH9 = tokens[tokens.length - 1] === weth9.address + + const value = inputIsWETH9 ? amountInMaximum : 0 + + const params = { + path: encodePath(tokens.slice().reverse(), new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)), + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountOut, + amountInMaximum, + } + + const data = [router.interface.encodeFunctionData('exactOutput', [params])] + if (inputIsWETH9) data.push(router.interface.encodeFunctionData('refundETH')) + if (outputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOut, trader.address])) + + return router.connect(trader).multicall(data, { value }) + } + + async function exactOutputSingle( + tokenIn: string, + tokenOut: string, + amountOut: number = 1, + amountInMaximum: number = 3, + sqrtPriceLimitX96?: BigNumber + ): Promise { + const inputIsWETH9 = tokenIn === weth9.address + const outputIsWETH9 = tokenOut === weth9.address + + const value = inputIsWETH9 ? amountInMaximum : 0 + + const params = { + tokenIn, + tokenOut, + fee: FeeAmount.MEDIUM, + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountOut, + amountInMaximum, + sqrtPriceLimitX96: sqrtPriceLimitX96 ?? 0, + } + + const data = [router.interface.encodeFunctionData('exactOutputSingle', [params])] + if (inputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [0, trader.address])) + if (outputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOut, trader.address])) + + return router.connect(trader).multicall(data, { value }) + } + + // TODO should really throw this in the fixture + beforeEach('intialize feeGrowthGlobals', async () => { + await exactInput([tokens[0].address, tokens[1].address], 1, 0) + await exactInput([tokens[1].address, tokens[0].address], 1, 0) + await exactInput([tokens[1].address, tokens[2].address], 1, 0) + await exactInput([tokens[2].address, tokens[1].address], 1, 0) + await exactInput([tokens[0].address, weth9.address], 1, 0) + await exactInput([weth9.address, tokens[0].address], 1, 0) + }) + + beforeEach('ensure feeGrowthGlobals are >0', async () => { + const slots = await Promise.all( + pools.map((pool) => + Promise.all([ + pool.feeGrowthGlobal0X128().then((f) => f.toString()), + pool.feeGrowthGlobal1X128().then((f) => f.toString()), + ]) + ) + ) + + expect(slots).to.deep.eq([ + ['340290874192793283295456993856614', '340290874192793283295456993856614'], + ['340290874192793283295456993856614', '340290874192793283295456993856614'], + ['340290874192793283295456993856614', '340290874192793283295456993856614'], + ]) + }) + + beforeEach('ensure ticks are 0 before', async () => { + const slots = await Promise.all(pools.map((pool) => pool.slot0().then(({ tick }) => tick))) + expect(slots).to.deep.eq([0, 0, 0]) + }) + + afterEach('ensure ticks are 0 after', async () => { + const slots = await Promise.all(pools.map((pool) => pool.slot0().then(({ tick }) => tick))) + expect(slots).to.deep.eq([0, 0, 0]) + }) + + describe('#exactInput', () => { + it('0 -> 1', async () => { + await snapshotGasCost(exactInput(tokens.slice(0, 2).map((token) => token.address))) + }) + + it('0 -> 1 minimal', async () => { + const calleeFactory = await ethers.getContractFactory('TestUniswapV3Callee') + const callee = await calleeFactory.deploy() + + await tokens[0].connect(trader).approve(callee.address, constants.MaxUint256) + await snapshotGasCost(callee.connect(trader).swapExact0For1(pools[0].address, 2, trader.address, '4295128740')) + }) + + it('0 -> 1 -> 2', async () => { + await snapshotGasCost( + exactInput( + tokens.map((token) => token.address), + 3 + ) + ) + }) + + it('WETH9 -> 0', async () => { + await snapshotGasCost( + exactInput( + [weth9.address, tokens[0].address], + weth9.address.toLowerCase() < tokens[0].address.toLowerCase() ? 2 : 3 + ) + ) + }) + + it('0 -> WETH9', async () => { + await snapshotGasCost( + exactInput( + [tokens[0].address, weth9.address], + tokens[0].address.toLowerCase() < weth9.address.toLowerCase() ? 2 : 3 + ) + ) + }) + + it('2 trades (via router)', async () => { + await weth9.connect(trader).deposit({ value: 3 }) + await weth9.connect(trader).approve(router.address, constants.MaxUint256) + const swap0 = { + path: encodePath([weth9.address, tokens[0].address], [FeeAmount.MEDIUM]), + recipient: constants.AddressZero, + deadline: 1, + amountIn: 3, + amountOutMinimum: 0, // save on calldata + } + + const swap1 = { + path: encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + recipient: constants.AddressZero, + deadline: 1, + amountIn: 3, + amountOutMinimum: 0, // save on calldata + } + + const data = [ + router.interface.encodeFunctionData('exactInput', [swap0]), + router.interface.encodeFunctionData('exactInput', [swap1]), + router.interface.encodeFunctionData('sweepToken', [tokens[0].address, 2, trader.address]), + ] + + await snapshotGasCost(router.connect(trader).multicall(data)) + }) + + it('3 trades (directly to sender)', async () => { + await weth9.connect(trader).deposit({ value: 3 }) + await weth9.connect(trader).approve(router.address, constants.MaxUint256) + const swap0 = { + path: encodePath([weth9.address, tokens[0].address], [FeeAmount.MEDIUM]), + recipient: trader.address, + deadline: 1, + amountIn: 3, + amountOutMinimum: 1, + } + + const swap1 = { + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + recipient: trader.address, + deadline: 1, + amountIn: 3, + amountOutMinimum: 1, + } + + const swap2 = { + path: encodePath([tokens[1].address, tokens[2].address], [FeeAmount.MEDIUM]), + recipient: trader.address, + deadline: 1, + amountIn: 3, + amountOutMinimum: 1, + } + + const data = [ + router.interface.encodeFunctionData('exactInput', [swap0]), + router.interface.encodeFunctionData('exactInput', [swap1]), + router.interface.encodeFunctionData('exactInput', [swap2]), + ] + + await snapshotGasCost(router.connect(trader).multicall(data)) + }) + }) + + it('3 trades (directly to sender)', async () => { + await weth9.connect(trader).deposit({ value: 3 }) + await weth9.connect(trader).approve(router.address, constants.MaxUint256) + const swap0 = { + path: encodePath([weth9.address, tokens[0].address], [FeeAmount.MEDIUM]), + recipient: trader.address, + deadline: 1, + amountIn: 3, + amountOutMinimum: 1, + } + + const swap1 = { + path: encodePath([tokens[1].address, tokens[0].address], [FeeAmount.MEDIUM]), + recipient: trader.address, + deadline: 1, + amountIn: 3, + amountOutMinimum: 1, + } + + const data = [ + router.interface.encodeFunctionData('exactInput', [swap0]), + router.interface.encodeFunctionData('exactInput', [swap1]), + ] + + await snapshotGasCost(router.connect(trader).multicall(data)) + }) + + describe('#exactInputSingle', () => { + it('0 -> 1', async () => { + await snapshotGasCost(exactInputSingle(tokens[0].address, tokens[1].address)) + }) + + it('WETH9 -> 0', async () => { + await snapshotGasCost( + exactInputSingle( + weth9.address, + tokens[0].address, + weth9.address.toLowerCase() < tokens[0].address.toLowerCase() ? 2 : 3 + ) + ) + }) + + it('0 -> WETH9', async () => { + await snapshotGasCost( + exactInputSingle( + tokens[0].address, + weth9.address, + tokens[0].address.toLowerCase() < weth9.address.toLowerCase() ? 2 : 3 + ) + ) + }) + }) + + describe('#exactOutput', () => { + it('0 -> 1', async () => { + await snapshotGasCost(exactOutput(tokens.slice(0, 2).map((token) => token.address))) + }) + + it('0 -> 1 -> 2', async () => { + await snapshotGasCost(exactOutput(tokens.map((token) => token.address))) + }) + + it('WETH9 -> 0', async () => { + await snapshotGasCost(exactOutput([weth9.address, tokens[0].address])) + }) + + it('0 -> WETH9', async () => { + await snapshotGasCost(exactOutput([tokens[0].address, weth9.address])) + }) + }) + + describe('#exactOutputSingle', () => { + it('0 -> 1', async () => { + await snapshotGasCost(exactOutputSingle(tokens[0].address, tokens[1].address)) + }) + + it('WETH9 -> 0', async () => { + await snapshotGasCost(exactOutputSingle(weth9.address, tokens[0].address)) + }) + + it('0 -> WETH9', async () => { + await snapshotGasCost(exactOutputSingle(tokens[0].address, weth9.address)) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/SwapRouter.spec.ts b/lib/uniswap/v3-periphery/test/SwapRouter.spec.ts new file mode 100644 index 0000000..a3d8f6d --- /dev/null +++ b/lib/uniswap/v3-periphery/test/SwapRouter.spec.ts @@ -0,0 +1,916 @@ +import { Fixture } from 'ethereum-waffle' +import { BigNumber, constants, Contract, ContractTransaction, Wallet } from 'ethers' +import { waffle, ethers } from 'hardhat' +import { IWETH9, MockTimeNonfungiblePositionManager, MockTimeSwapRouter, TestERC20 } from '../typechain' +import completeFixture from './shared/completeFixture' +import { FeeAmount, TICK_SPACINGS } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expandTo18Decimals } from './shared/expandTo18Decimals' +import { expect } from './shared/expect' +import { encodePath } from './shared/path' +import { getMaxTick, getMinTick } from './shared/ticks' +import { computePoolAddress } from './shared/computePoolAddress' + +describe('SwapRouter', function () { + this.timeout(40000) + let wallet: Wallet + let trader: Wallet + + const swapRouterFixture: Fixture<{ + weth9: IWETH9 + factory: Contract + router: MockTimeSwapRouter + nft: MockTimeNonfungiblePositionManager + tokens: [TestERC20, TestERC20, TestERC20] + }> = async (wallets, provider) => { + const { weth9, factory, router, tokens, nft } = await completeFixture(wallets, provider) + + // approve & fund wallets + for (const token of tokens) { + await token.approve(router.address, constants.MaxUint256) + await token.approve(nft.address, constants.MaxUint256) + await token.connect(trader).approve(router.address, constants.MaxUint256) + await token.transfer(trader.address, expandTo18Decimals(1_000_000)) + } + + return { + weth9, + factory, + router, + tokens, + nft, + } + } + + let factory: Contract + let weth9: IWETH9 + let router: MockTimeSwapRouter + let nft: MockTimeNonfungiblePositionManager + let tokens: [TestERC20, TestERC20, TestERC20] + let getBalances: ( + who: string + ) => Promise<{ + weth9: BigNumber + token0: BigNumber + token1: BigNumber + token2: BigNumber + }> + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + ;[wallet, trader] = await (ethers as any).getSigners() + loadFixture = waffle.createFixtureLoader([wallet, trader]) + }) + + // helper for getting weth and token balances + beforeEach('load fixture', async () => { + ;({ router, weth9, factory, tokens, nft } = await loadFixture(swapRouterFixture)) + + getBalances = async (who: string) => { + const balances = await Promise.all([ + weth9.balanceOf(who), + tokens[0].balanceOf(who), + tokens[1].balanceOf(who), + tokens[2].balanceOf(who), + ]) + return { + weth9: balances[0], + token0: balances[1], + token1: balances[2], + token2: balances[3], + } + } + }) + + // ensure the swap router never ends up with a balance + afterEach('load fixture', async () => { + const balances = await getBalances(router.address) + expect(Object.values(balances).every((b) => b.eq(0))).to.be.eq(true) + const balance = await waffle.provider.getBalance(router.address) + expect(balance.eq(0)).to.be.eq(true) + }) + + it('bytecode size', async () => { + expect(((await router.provider.getCode(router.address)).length - 2) / 2).to.matchSnapshot() + }) + + describe('swaps', () => { + const liquidity = 1000000 + async function createPool(tokenAddressA: string, tokenAddressB: string) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary( + tokenAddressA, + tokenAddressB, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallet.address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams) + } + + async function createPoolWETH9(tokenAddress: string) { + await weth9.deposit({ value: liquidity }) + await weth9.approve(nft.address, constants.MaxUint256) + return createPool(weth9.address, tokenAddress) + } + + beforeEach('create 0-1 and 1-2 pools', async () => { + await createPool(tokens[0].address, tokens[1].address) + await createPool(tokens[1].address, tokens[2].address) + }) + + describe('#exactInput', () => { + async function exactInput( + tokens: string[], + amountIn: number = 3, + amountOutMinimum: number = 1 + ): Promise { + const inputIsWETH = weth9.address === tokens[0] + const outputIsWETH9 = tokens[tokens.length - 1] === weth9.address + + const value = inputIsWETH ? amountIn : 0 + + const params = { + path: encodePath(tokens, new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)), + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountIn, + amountOutMinimum, + } + + const data = [router.interface.encodeFunctionData('exactInput', [params])] + if (outputIsWETH9) + data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOutMinimum, trader.address])) + + // ensure that the swap fails if the limit is any tighter + params.amountOutMinimum += 1 + await expect(router.connect(trader).exactInput(params, { value })).to.be.revertedWith('Too little received') + params.amountOutMinimum -= 1 + + // optimized for the gas test + return data.length === 1 + ? router.connect(trader).exactInput(params, { value }) + : router.connect(trader).multicall(data, { value }) + } + + describe('single-pool', () => { + it('0 -> 1', async () => { + const pool = await factory.getPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactInput(tokens.slice(0, 2).map((token) => token.address)) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.add(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.sub(1)) + }) + + it('1 -> 0', async () => { + const pool = await factory.getPool(tokens[1].address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactInput( + tokens + .slice(0, 2) + .reverse() + .map((token) => token.address) + ) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.sub(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.add(3)) + }) + }) + + describe('multi-pool', () => { + it('0 -> 1 -> 2', async () => { + const traderBefore = await getBalances(trader.address) + + await exactInput( + tokens.map((token) => token.address), + 5, + 1 + ) + + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(5)) + expect(traderAfter.token2).to.be.eq(traderBefore.token2.add(1)) + }) + + it('2 -> 1 -> 0', async () => { + const traderBefore = await getBalances(trader.address) + + await exactInput(tokens.map((token) => token.address).reverse(), 5, 1) + + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token2).to.be.eq(traderBefore.token2.sub(5)) + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + }) + + it('events', async () => { + await expect( + exactInput( + tokens.map((token) => token.address), + 5, + 1 + ) + ) + .to.emit(tokens[0], 'Transfer') + .withArgs( + trader.address, + computePoolAddress(factory.address, [tokens[0].address, tokens[1].address], FeeAmount.MEDIUM), + 5 + ) + .to.emit(tokens[1], 'Transfer') + .withArgs( + computePoolAddress(factory.address, [tokens[0].address, tokens[1].address], FeeAmount.MEDIUM), + router.address, + 3 + ) + .to.emit(tokens[1], 'Transfer') + .withArgs( + router.address, + computePoolAddress(factory.address, [tokens[1].address, tokens[2].address], FeeAmount.MEDIUM), + 3 + ) + .to.emit(tokens[2], 'Transfer') + .withArgs( + computePoolAddress(factory.address, [tokens[1].address, tokens[2].address], FeeAmount.MEDIUM), + trader.address, + 1 + ) + }) + }) + + describe('ETH input', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + }) + + it('WETH9 -> 0', async () => { + const pool = await factory.getPool(weth9.address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactInput([weth9.address, tokens[0].address])) + .to.emit(weth9, 'Deposit') + .withArgs(router.address, 3) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.add(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + }) + + it('WETH9 -> 0 -> 1', async () => { + const traderBefore = await getBalances(trader.address) + + await expect(exactInput([weth9.address, tokens[0].address, tokens[1].address], 5)) + .to.emit(weth9, 'Deposit') + .withArgs(router.address, 5) + + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token1).to.be.eq(traderBefore.token1.add(1)) + }) + }) + }) + + describe('ETH output', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + await createPoolWETH9(tokens[1].address) + }) + + it('0 -> WETH9', async () => { + const pool = await factory.getPool(tokens[0].address, weth9.address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactInput([tokens[0].address, weth9.address])) + .to.emit(weth9, 'Withdrawal') + .withArgs(router.address, 1) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.sub(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + }) + + it('0 -> 1 -> WETH9', async () => { + // get balances before + const traderBefore = await getBalances(trader.address) + + await expect(exactInput([tokens[0].address, tokens[1].address, weth9.address], 5)) + .to.emit(weth9, 'Withdrawal') + .withArgs(router.address, 1) + + // get balances after + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(5)) + }) + }) + }) + }) + + describe('#exactInputSingle', () => { + async function exactInputSingle( + tokenIn: string, + tokenOut: string, + amountIn: number = 3, + amountOutMinimum: number = 1, + sqrtPriceLimitX96?: BigNumber + ): Promise { + const inputIsWETH = weth9.address === tokenIn + const outputIsWETH9 = tokenOut === weth9.address + + const value = inputIsWETH ? amountIn : 0 + + const params = { + tokenIn, + tokenOut, + fee: FeeAmount.MEDIUM, + sqrtPriceLimitX96: + sqrtPriceLimitX96 ?? tokenIn.toLowerCase() < tokenOut.toLowerCase() + ? BigNumber.from('4295128740') + : BigNumber.from('1461446703485210103287273052203988822378723970341'), + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountIn, + amountOutMinimum, + } + + const data = [router.interface.encodeFunctionData('exactInputSingle', [params])] + if (outputIsWETH9) + data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOutMinimum, trader.address])) + + // ensure that the swap fails if the limit is any tighter + params.amountOutMinimum += 1 + await expect(router.connect(trader).exactInputSingle(params, { value })).to.be.revertedWith( + 'Too little received' + ) + params.amountOutMinimum -= 1 + + // optimized for the gas test + return data.length === 1 + ? router.connect(trader).exactInputSingle(params, { value }) + : router.connect(trader).multicall(data, { value }) + } + + it('0 -> 1', async () => { + const pool = await factory.getPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactInputSingle(tokens[0].address, tokens[1].address) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.add(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.sub(1)) + }) + + it('1 -> 0', async () => { + const pool = await factory.getPool(tokens[1].address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactInputSingle(tokens[1].address, tokens[0].address) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.sub(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.add(3)) + }) + + describe('ETH input', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + }) + + it('WETH9 -> 0', async () => { + const pool = await factory.getPool(weth9.address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactInputSingle(weth9.address, tokens[0].address)) + .to.emit(weth9, 'Deposit') + .withArgs(router.address, 3) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.add(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + }) + }) + }) + + describe('ETH output', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + await createPoolWETH9(tokens[1].address) + }) + + it('0 -> WETH9', async () => { + const pool = await factory.getPool(tokens[0].address, weth9.address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactInputSingle(tokens[0].address, weth9.address)) + .to.emit(weth9, 'Withdrawal') + .withArgs(router.address, 1) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.sub(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + }) + }) + }) + }) + + describe('#exactOutput', () => { + async function exactOutput( + tokens: string[], + amountOut: number = 1, + amountInMaximum: number = 3 + ): Promise { + const inputIsWETH9 = tokens[0] === weth9.address + const outputIsWETH9 = tokens[tokens.length - 1] === weth9.address + + const value = inputIsWETH9 ? amountInMaximum : 0 + + const params = { + path: encodePath(tokens.slice().reverse(), new Array(tokens.length - 1).fill(FeeAmount.MEDIUM)), + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountOut, + amountInMaximum, + } + + const data = [router.interface.encodeFunctionData('exactOutput', [params])] + if (inputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [0, trader.address])) + if (outputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOut, trader.address])) + + // ensure that the swap fails if the limit is any tighter + params.amountInMaximum -= 1 + await expect(router.connect(trader).exactOutput(params, { value })).to.be.revertedWith('Too much requested') + params.amountInMaximum += 1 + + return router.connect(trader).multicall(data, { value }) + } + + describe('single-pool', () => { + it('0 -> 1', async () => { + const pool = await factory.getPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactOutput(tokens.slice(0, 2).map((token) => token.address)) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.add(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.sub(1)) + }) + + it('1 -> 0', async () => { + const pool = await factory.getPool(tokens[1].address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactOutput( + tokens + .slice(0, 2) + .reverse() + .map((token) => token.address) + ) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.sub(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.add(3)) + }) + }) + + describe('multi-pool', () => { + it('0 -> 1 -> 2', async () => { + const traderBefore = await getBalances(trader.address) + + await exactOutput( + tokens.map((token) => token.address), + 1, + 5 + ) + + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(5)) + expect(traderAfter.token2).to.be.eq(traderBefore.token2.add(1)) + }) + + it('2 -> 1 -> 0', async () => { + const traderBefore = await getBalances(trader.address) + + await exactOutput(tokens.map((token) => token.address).reverse(), 1, 5) + + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token2).to.be.eq(traderBefore.token2.sub(5)) + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + }) + + it('events', async () => { + await expect( + exactOutput( + tokens.map((token) => token.address), + 1, + 5 + ) + ) + .to.emit(tokens[2], 'Transfer') + .withArgs( + computePoolAddress(factory.address, [tokens[2].address, tokens[1].address], FeeAmount.MEDIUM), + trader.address, + 1 + ) + .to.emit(tokens[1], 'Transfer') + .withArgs( + computePoolAddress(factory.address, [tokens[1].address, tokens[0].address], FeeAmount.MEDIUM), + computePoolAddress(factory.address, [tokens[2].address, tokens[1].address], FeeAmount.MEDIUM), + 3 + ) + .to.emit(tokens[0], 'Transfer') + .withArgs( + trader.address, + computePoolAddress(factory.address, [tokens[1].address, tokens[0].address], FeeAmount.MEDIUM), + 5 + ) + }) + }) + + describe('ETH input', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + }) + + it('WETH9 -> 0', async () => { + const pool = await factory.getPool(weth9.address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactOutput([weth9.address, tokens[0].address])) + .to.emit(weth9, 'Deposit') + .withArgs(router.address, 3) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.add(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + }) + + it('WETH9 -> 0 -> 1', async () => { + const traderBefore = await getBalances(trader.address) + + await expect(exactOutput([weth9.address, tokens[0].address, tokens[1].address], 1, 5)) + .to.emit(weth9, 'Deposit') + .withArgs(router.address, 5) + + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token1).to.be.eq(traderBefore.token1.add(1)) + }) + }) + }) + + describe('ETH output', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + await createPoolWETH9(tokens[1].address) + }) + + it('0 -> WETH9', async () => { + const pool = await factory.getPool(tokens[0].address, weth9.address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactOutput([tokens[0].address, weth9.address])) + .to.emit(weth9, 'Withdrawal') + .withArgs(router.address, 1) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.sub(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + }) + + it('0 -> 1 -> WETH9', async () => { + // get balances before + const traderBefore = await getBalances(trader.address) + + await expect(exactOutput([tokens[0].address, tokens[1].address, weth9.address], 1, 5)) + .to.emit(weth9, 'Withdrawal') + .withArgs(router.address, 1) + + // get balances after + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(5)) + }) + }) + }) + }) + + describe('#exactOutputSingle', () => { + async function exactOutputSingle( + tokenIn: string, + tokenOut: string, + amountOut: number = 1, + amountInMaximum: number = 3, + sqrtPriceLimitX96?: BigNumber + ): Promise { + const inputIsWETH9 = tokenIn === weth9.address + const outputIsWETH9 = tokenOut === weth9.address + + const value = inputIsWETH9 ? amountInMaximum : 0 + + const params = { + tokenIn, + tokenOut, + fee: FeeAmount.MEDIUM, + recipient: outputIsWETH9 ? constants.AddressZero : trader.address, + deadline: 1, + amountOut, + amountInMaximum, + sqrtPriceLimitX96: + sqrtPriceLimitX96 ?? tokenIn.toLowerCase() < tokenOut.toLowerCase() + ? BigNumber.from('4295128740') + : BigNumber.from('1461446703485210103287273052203988822378723970341'), + } + + const data = [router.interface.encodeFunctionData('exactOutputSingle', [params])] + if (inputIsWETH9) data.push(router.interface.encodeFunctionData('refundETH')) + if (outputIsWETH9) data.push(router.interface.encodeFunctionData('unwrapWETH9', [amountOut, trader.address])) + + // ensure that the swap fails if the limit is any tighter + params.amountInMaximum -= 1 + await expect(router.connect(trader).exactOutputSingle(params, { value })).to.be.revertedWith( + 'Too much requested' + ) + params.amountInMaximum += 1 + + return router.connect(trader).multicall(data, { value }) + } + + it('0 -> 1', async () => { + const pool = await factory.getPool(tokens[0].address, tokens[1].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactOutputSingle(tokens[0].address, tokens[1].address) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.add(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.sub(1)) + }) + + it('1 -> 0', async () => { + const pool = await factory.getPool(tokens[1].address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await exactOutputSingle(tokens[1].address, tokens[0].address) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(traderAfter.token1).to.be.eq(traderBefore.token1.sub(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + expect(poolAfter.token1).to.be.eq(poolBefore.token1.add(3)) + }) + + describe('ETH input', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + }) + + it('WETH9 -> 0', async () => { + const pool = await factory.getPool(weth9.address, tokens[0].address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactOutputSingle(weth9.address, tokens[0].address)) + .to.emit(weth9, 'Deposit') + .withArgs(router.address, 3) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.add(1)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.add(3)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.sub(1)) + }) + }) + }) + + describe('ETH output', () => { + describe('WETH9', () => { + beforeEach(async () => { + await createPoolWETH9(tokens[0].address) + await createPoolWETH9(tokens[1].address) + }) + + it('0 -> WETH9', async () => { + const pool = await factory.getPool(tokens[0].address, weth9.address, FeeAmount.MEDIUM) + + // get balances before + const poolBefore = await getBalances(pool) + const traderBefore = await getBalances(trader.address) + + await expect(exactOutputSingle(tokens[0].address, weth9.address)) + .to.emit(weth9, 'Withdrawal') + .withArgs(router.address, 1) + + // get balances after + const poolAfter = await getBalances(pool) + const traderAfter = await getBalances(trader.address) + + expect(traderAfter.token0).to.be.eq(traderBefore.token0.sub(3)) + expect(poolAfter.weth9).to.be.eq(poolBefore.weth9.sub(1)) + expect(poolAfter.token0).to.be.eq(poolBefore.token0.add(3)) + }) + }) + }) + }) + + describe('*WithFee', () => { + const feeRecipient = '0xfEE0000000000000000000000000000000000000' + + it('#sweepTokenWithFee', async () => { + const amountOutMinimum = 100 + const params = { + path: encodePath([tokens[0].address, tokens[1].address], [FeeAmount.MEDIUM]), + recipient: router.address, + deadline: 1, + amountIn: 102, + amountOutMinimum, + } + + const data = [ + router.interface.encodeFunctionData('exactInput', [params]), + router.interface.encodeFunctionData('sweepTokenWithFee', [ + tokens[1].address, + amountOutMinimum, + trader.address, + 100, + feeRecipient, + ]), + ] + + await router.connect(trader).multicall(data) + + const balance = await tokens[1].balanceOf(feeRecipient) + expect(balance.eq(1)).to.be.eq(true) + }) + + it('#unwrapWETH9WithFee', async () => { + const startBalance = await waffle.provider.getBalance(feeRecipient) + await createPoolWETH9(tokens[0].address) + + const amountOutMinimum = 100 + const params = { + path: encodePath([tokens[0].address, weth9.address], [FeeAmount.MEDIUM]), + recipient: router.address, + deadline: 1, + amountIn: 102, + amountOutMinimum, + } + + const data = [ + router.interface.encodeFunctionData('exactInput', [params]), + router.interface.encodeFunctionData('unwrapWETH9WithFee', [ + amountOutMinimum, + trader.address, + 100, + feeRecipient, + ]), + ] + + await router.connect(trader).multicall(data) + const endBalance = await waffle.provider.getBalance(feeRecipient) + expect(endBalance.sub(startBalance).eq(1)).to.be.eq(true) + }) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/TickLens.spec.ts b/lib/uniswap/v3-periphery/test/TickLens.spec.ts new file mode 100644 index 0000000..4a927d7 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/TickLens.spec.ts @@ -0,0 +1,223 @@ +import { Fixture } from 'ethereum-waffle' +import { BigNumber, BigNumberish, constants, Contract, ContractTransaction, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { MockTimeNonfungiblePositionManager, TestERC20, TickLensTest } from '../typechain' +import completeFixture from './shared/completeFixture' +import { FeeAmount, TICK_SPACINGS } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import { expect } from './shared/expect' +import { getMaxTick, getMinTick } from './shared/ticks' +import { computePoolAddress } from './shared/computePoolAddress' +import snapshotGasCost from './shared/snapshotGasCost' + +describe('TickLens', () => { + let wallets: Wallet[] + + const nftFixture: Fixture<{ + factory: Contract + nft: MockTimeNonfungiblePositionManager + tokens: [TestERC20, TestERC20, TestERC20] + }> = async (wallets, provider) => { + const { factory, tokens, nft } = await completeFixture(wallets, provider) + + for (const token of tokens) { + await token.approve(nft.address, constants.MaxUint256) + } + + return { + factory, + nft, + tokens, + } + } + + let factory: Contract + let nft: MockTimeNonfungiblePositionManager + let tokens: [TestERC20, TestERC20, TestERC20] + let poolAddress: string + let tickLens: TickLensTest + + let loadFixture: ReturnType + + before('create fixture loader', async () => { + wallets = await (ethers as any).getSigners() + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ factory, tokens, nft } = await loadFixture(nftFixture)) + }) + + describe('#getPopulatedTicksInWord', () => { + const fullRangeLiquidity = 1000000 + async function createPool(tokenAddressA: string, tokenAddressB: string) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary( + tokenAddressA, + tokenAddressB, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallets[0].address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams) + } + + async function mint(tickLower: number, tickUpper: number, amountBothDesired: BigNumberish): Promise { + const mintParams = { + token0: tokens[0].address, + token1: tokens[1].address, + fee: FeeAmount.MEDIUM, + tickLower, + tickUpper, + amount0Desired: amountBothDesired, + amount1Desired: amountBothDesired, + amount0Min: 0, + amount1Min: 0, + recipient: wallets[0].address, + deadline: 1, + } + + const { liquidity } = await nft.callStatic.mint(mintParams) + + await nft.mint(mintParams) + return liquidity.toNumber() + } + + beforeEach(async () => { + await createPool(tokens[0].address, tokens[1].address) + poolAddress = computePoolAddress(factory.address, [tokens[0].address, tokens[1].address], FeeAmount.MEDIUM) + }) + + beforeEach(async () => { + const lensFactory = await ethers.getContractFactory('TickLensTest') + tickLens = (await lensFactory.deploy()) as TickLensTest + }) + + function getTickBitmapIndex(tick: BigNumberish, tickSpacing: number): BigNumber { + const intermediate = BigNumber.from(tick).div(tickSpacing) + // see https://docs.soliditylang.org/en/v0.7.6/types.html#shifts + return intermediate.lt(0) ? intermediate.add(1).div(BigNumber.from(2).pow(8)).sub(1) : intermediate.shr(8) + } + + it('works for min/max', async () => { + const [min] = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + + const [max] = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + + expect(min.tick).to.be.eq(getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + expect(min.liquidityNet).to.be.eq(fullRangeLiquidity) + expect(min.liquidityGross).to.be.eq(fullRangeLiquidity) + + expect(max.tick).to.be.eq(getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + expect(max.liquidityNet).to.be.eq(fullRangeLiquidity * -1) + expect(min.liquidityGross).to.be.eq(fullRangeLiquidity) + }) + + it('works for min/max and -2/-1/0/1', async () => { + const minus = -TICK_SPACINGS[FeeAmount.MEDIUM] + const plus = -minus + + const liquidity0 = await mint(minus * 2, minus, 2) + const liquidity1 = await mint(minus * 2, 0, 3) + const liquidity2 = await mint(minus * 2, plus, 5) + const liquidity3 = await mint(minus, 0, 7) + const liquidity4 = await mint(minus, plus, 11) + const liquidity5 = await mint(0, plus, 13) + + const [min] = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + + const [negativeOne, negativeTwo] = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(minus, TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + + const [one, zero] = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(plus, TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + + const [max] = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + + expect(min.tick).to.be.eq(getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + expect(min.liquidityNet).to.be.eq(fullRangeLiquidity) + expect(min.liquidityGross).to.be.eq(fullRangeLiquidity) + + expect(negativeTwo.tick).to.be.eq(minus * 2) + expect(negativeTwo.liquidityNet).to.be.eq(liquidity0 + liquidity1 + liquidity2) + expect(negativeTwo.liquidityGross).to.be.eq(liquidity0 + liquidity1 + liquidity2) + + expect(negativeOne.tick).to.be.eq(minus) + expect(negativeOne.liquidityNet).to.be.eq(liquidity3 + liquidity4 - liquidity0) + expect(negativeOne.liquidityGross).to.be.eq(liquidity3 + liquidity4 + liquidity0) + + expect(zero.tick).to.be.eq(0) + expect(zero.liquidityNet).to.be.eq(liquidity5 - liquidity1 - liquidity3) + expect(zero.liquidityGross).to.be.eq(liquidity5 + liquidity1 + liquidity3) + + expect(one.tick).to.be.eq(plus) + expect(one.liquidityNet).to.be.eq(-liquidity2 - liquidity4 - liquidity5) + expect(one.liquidityGross).to.be.eq(liquidity2 + liquidity4 + liquidity5) + + expect(max.tick).to.be.eq(getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM])) + expect(max.liquidityNet).to.be.eq(fullRangeLiquidity * -1) + expect(max.liquidityGross).to.be.eq(fullRangeLiquidity) + }) + + it('gas for single populated tick', async () => { + await snapshotGasCost( + tickLens.getGasCostOfGetPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + ) + }) + + it('fully populated ticks', async () => { + // fully populate a word + for (let i = 0; i < 128; i++) { + await mint(i * TICK_SPACINGS[FeeAmount.MEDIUM], (255 - i) * TICK_SPACINGS[FeeAmount.MEDIUM], 100) + } + + const ticks = await tickLens.getPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(0, TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + expect(ticks.length).to.be.eq(256) + + await snapshotGasCost( + tickLens.getGasCostOfGetPopulatedTicksInWord( + poolAddress, + getTickBitmapIndex(0, TICK_SPACINGS[FeeAmount.MEDIUM]) + ) + ) + }).timeout(300_000) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/V3Migrator.spec.ts b/lib/uniswap/v3-periphery/test/V3Migrator.spec.ts new file mode 100644 index 0000000..f113b27 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/V3Migrator.spec.ts @@ -0,0 +1,446 @@ +import { Fixture } from 'ethereum-waffle' +import { constants, Contract, Wallet } from 'ethers' +import { ethers, waffle } from 'hardhat' +import { + IUniswapV2Pair, + IUniswapV3Factory, + IWETH9, + MockTimeNonfungiblePositionManager, + TestERC20, + V3Migrator, +} from '../typechain' +import completeFixture from './shared/completeFixture' +import { v2FactoryFixture } from './shared/externalFixtures' + +import { abi as PAIR_V2_ABI } from '@uniswap/v2-core/build/UniswapV2Pair.json' +import { expect } from 'chai' +import { FeeAmount } from './shared/constants' +import { encodePriceSqrt } from './shared/encodePriceSqrt' +import snapshotGasCost from './shared/snapshotGasCost' +import { sortedTokens } from './shared/tokenSort' +import { getMaxTick, getMinTick } from './shared/ticks' + +describe('V3Migrator', () => { + let wallet: Wallet + + const migratorFixture: Fixture<{ + factoryV2: Contract + factoryV3: IUniswapV3Factory + token: TestERC20 + weth9: IWETH9 + nft: MockTimeNonfungiblePositionManager + migrator: V3Migrator + }> = async (wallets, provider) => { + const { factory, tokens, nft, weth9 } = await completeFixture(wallets, provider) + + const { factory: factoryV2 } = await v2FactoryFixture(wallets, provider) + + const token = tokens[0] + await token.approve(factoryV2.address, constants.MaxUint256) + await weth9.deposit({ value: 10000 }) + await weth9.approve(nft.address, constants.MaxUint256) + + // deploy the migrator + const migrator = (await (await ethers.getContractFactory('V3Migrator')).deploy( + factory.address, + weth9.address, + nft.address + )) as V3Migrator + + return { + factoryV2, + factoryV3: factory, + token, + weth9, + nft, + migrator, + } + } + + let factoryV2: Contract + let factoryV3: IUniswapV3Factory + let token: TestERC20 + let weth9: IWETH9 + let nft: MockTimeNonfungiblePositionManager + let migrator: V3Migrator + let pair: IUniswapV2Pair + + let loadFixture: ReturnType + + const expectedLiquidity = 10000 - 1000 + + before('create fixture loader', async () => { + const wallets = await (ethers as any).getSigners() + wallet = wallets[0] + + loadFixture = waffle.createFixtureLoader(wallets) + }) + + beforeEach('load fixture', async () => { + ;({ factoryV2, factoryV3, token, weth9, nft, migrator } = await loadFixture(migratorFixture)) + }) + + beforeEach('add V2 liquidity', async () => { + await factoryV2.createPair(token.address, weth9.address) + + const pairAddress = await factoryV2.getPair(token.address, weth9.address) + + pair = new ethers.Contract(pairAddress, PAIR_V2_ABI, wallet) as IUniswapV2Pair + + await token.transfer(pair.address, 10000) + await weth9.transfer(pair.address, 10000) + + await pair.mint(wallet.address) + + expect(await pair.balanceOf(wallet.address)).to.be.eq(expectedLiquidity) + }) + + afterEach('ensure allowances are cleared', async () => { + const allowanceToken = await token.allowance(migrator.address, nft.address) + const allowanceWETH9 = await weth9.allowance(migrator.address, nft.address) + expect(allowanceToken).to.be.eq(0) + expect(allowanceWETH9).to.be.eq(0) + }) + + afterEach('ensure balances are cleared', async () => { + const balanceToken = await token.balanceOf(migrator.address) + const balanceWETH9 = await weth9.balanceOf(migrator.address) + expect(balanceToken).to.be.eq(0) + expect(balanceWETH9).to.be.eq(0) + }) + + afterEach('ensure eth balance is cleared', async () => { + const balanceETH = await ethers.provider.getBalance(migrator.address) + expect(balanceETH).to.be.eq(0) + }) + + describe('#migrate', () => { + let tokenLower: boolean + beforeEach(() => { + tokenLower = token.address.toLowerCase() < weth9.address.toLowerCase() + }) + + it('fails if v3 pool is not initialized', async () => { + await pair.approve(migrator.address, expectedLiquidity) + await expect( + migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: -1, + tickUpper: 1, + amount0Min: 9000, + amount1Min: 9000, + recipient: wallet.address, + deadline: 1, + refundAsETH: false, + }) + ).to.be.reverted + }) + + it('works once v3 pool is initialized', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await pair.approve(migrator.address, expectedLiquidity) + await migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 9000, + amount1Min: 9000, + recipient: wallet.address, + deadline: 1, + refundAsETH: false, + }) + + const position = await nft.positions(1) + expect(position.liquidity).to.be.eq(9000) + + const poolAddress = await factoryV3.getPool(token.address, weth9.address, FeeAmount.MEDIUM) + expect(await token.balanceOf(poolAddress)).to.be.eq(9000) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(9000) + }) + + it('works for partial', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + const tokenBalanceBefore = await token.balanceOf(wallet.address) + const weth9BalanceBefore = await weth9.balanceOf(wallet.address) + + await pair.approve(migrator.address, expectedLiquidity) + await migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 50, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 4500, + amount1Min: 4500, + recipient: wallet.address, + deadline: 1, + refundAsETH: false, + }) + + const tokenBalanceAfter = await token.balanceOf(wallet.address) + const weth9BalanceAfter = await weth9.balanceOf(wallet.address) + + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(4500) + expect(weth9BalanceAfter.sub(weth9BalanceBefore)).to.be.eq(4500) + + const position = await nft.positions(1) + expect(position.liquidity).to.be.eq(4500) + + const poolAddress = await factoryV3.getPool(token.address, weth9.address, FeeAmount.MEDIUM) + expect(await token.balanceOf(poolAddress)).to.be.eq(4500) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(4500) + }) + + it('double the price', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(2, 1) + ) + + const tokenBalanceBefore = await token.balanceOf(wallet.address) + const weth9BalanceBefore = await weth9.balanceOf(wallet.address) + + await pair.approve(migrator.address, expectedLiquidity) + await migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 4500, + amount1Min: 8999, + recipient: wallet.address, + deadline: 1, + refundAsETH: false, + }) + + const tokenBalanceAfter = await token.balanceOf(wallet.address) + const weth9BalanceAfter = await weth9.balanceOf(wallet.address) + + const position = await nft.positions(1) + expect(position.liquidity).to.be.eq(6363) + + const poolAddress = await factoryV3.getPool(token.address, weth9.address, FeeAmount.MEDIUM) + if (token.address.toLowerCase() < weth9.address.toLowerCase()) { + expect(await token.balanceOf(poolAddress)).to.be.eq(4500) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(4500) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(8999) + expect(weth9BalanceAfter.sub(weth9BalanceBefore)).to.be.eq(1) + } else { + expect(await token.balanceOf(poolAddress)).to.be.eq(8999) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(1) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(4500) + expect(weth9BalanceAfter.sub(weth9BalanceBefore)).to.be.eq(4500) + } + }) + + it('half the price', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 2) + ) + + const tokenBalanceBefore = await token.balanceOf(wallet.address) + const weth9BalanceBefore = await weth9.balanceOf(wallet.address) + + await pair.approve(migrator.address, expectedLiquidity) + await migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 8999, + amount1Min: 4500, + recipient: wallet.address, + deadline: 1, + refundAsETH: false, + }) + + const tokenBalanceAfter = await token.balanceOf(wallet.address) + const weth9BalanceAfter = await weth9.balanceOf(wallet.address) + + const position = await nft.positions(1) + expect(position.liquidity).to.be.eq(6363) + + const poolAddress = await factoryV3.getPool(token.address, weth9.address, FeeAmount.MEDIUM) + if (token.address.toLowerCase() < weth9.address.toLowerCase()) { + expect(await token.balanceOf(poolAddress)).to.be.eq(8999) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(1) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(4500) + expect(weth9BalanceAfter.sub(weth9BalanceBefore)).to.be.eq(4500) + } else { + expect(await token.balanceOf(poolAddress)).to.be.eq(4500) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(4500) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(8999) + expect(weth9BalanceAfter.sub(weth9BalanceBefore)).to.be.eq(1) + } + }) + + it('double the price - as ETH', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(2, 1) + ) + + const tokenBalanceBefore = await token.balanceOf(wallet.address) + + await pair.approve(migrator.address, expectedLiquidity) + await expect( + migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 4500, + amount1Min: 8999, + recipient: wallet.address, + deadline: 1, + refundAsETH: true, + }) + ) + .to.emit(weth9, 'Withdrawal') + .withArgs(migrator.address, tokenLower ? 1 : 4500) + + const tokenBalanceAfter = await token.balanceOf(wallet.address) + + const position = await nft.positions(1) + expect(position.liquidity).to.be.eq(6363) + + const poolAddress = await factoryV3.getPool(token.address, weth9.address, FeeAmount.MEDIUM) + if (tokenLower) { + expect(await token.balanceOf(poolAddress)).to.be.eq(4500) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(4500) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(8999) + } else { + expect(await token.balanceOf(poolAddress)).to.be.eq(8999) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(1) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(4500) + } + }) + + it('half the price - as ETH', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 2) + ) + + const tokenBalanceBefore = await token.balanceOf(wallet.address) + + await pair.approve(migrator.address, expectedLiquidity) + await expect( + migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 8999, + amount1Min: 4500, + recipient: wallet.address, + deadline: 1, + refundAsETH: true, + }) + ) + .to.emit(weth9, 'Withdrawal') + .withArgs(migrator.address, tokenLower ? 4500 : 1) + + const tokenBalanceAfter = await token.balanceOf(wallet.address) + + const position = await nft.positions(1) + expect(position.liquidity).to.be.eq(6363) + + const poolAddress = await factoryV3.getPool(token.address, weth9.address, FeeAmount.MEDIUM) + if (tokenLower) { + expect(await token.balanceOf(poolAddress)).to.be.eq(8999) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(1) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(4500) + } else { + expect(await token.balanceOf(poolAddress)).to.be.eq(4500) + expect(tokenBalanceAfter.sub(tokenBalanceBefore)).to.be.eq(4500) + expect(await weth9.balanceOf(poolAddress)).to.be.eq(8999) + } + }) + + it('gas', async () => { + const [token0, token1] = sortedTokens(weth9, token) + await migrator.createAndInitializePoolIfNecessary( + token0.address, + token1.address, + FeeAmount.MEDIUM, + encodePriceSqrt(1, 1) + ) + + await pair.approve(migrator.address, expectedLiquidity) + await snapshotGasCost( + migrator.migrate({ + pair: pair.address, + liquidityToMigrate: expectedLiquidity, + percentageToMigrate: 100, + token0: tokenLower ? token.address : weth9.address, + token1: tokenLower ? weth9.address : token.address, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(FeeAmount.MEDIUM), + tickUpper: getMaxTick(FeeAmount.MEDIUM), + amount0Min: 9000, + amount1Min: 9000, + recipient: wallet.address, + deadline: 1, + refundAsETH: false, + }) + ) + }) + }) +}) diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/Base64.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/Base64.spec.ts.snap new file mode 100644 index 0000000..fd54e5c --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/Base64.spec.ts.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Base64 #encode gas cost of encode() 1`] = `1497`; + +exports[`Base64 #encode gas cost of encode(aLpHaBeT) 1`] = `1256`; + +exports[`Base64 #encode gas cost of encode(alphabet soup) 1`] = `1727`; + +exports[`Base64 #encode gas cost of encode(f) 1`] = `763`; + +exports[`Base64 #encode gas cost of encode(fo) 1`] = `774`; + +exports[`Base64 #encode gas cost of encode(foo) 1`] = `769`; + +exports[`Base64 #encode gas cost of encode(foob) 1`] = `1004`; + +exports[`Base64 #encode gas cost of encode(fooba) 1`] = `1015`; + +exports[`Base64 #encode gas cost of encode(foobar) 1`] = `1010`; + +exports[`Base64 #encode gas cost of encode(includes +newlines) 1`] = `1979`; + +exports[`Base64 #encode gas cost of encode(test string) 1`] = `1497`; + +exports[`Base64 #encode gas cost of encode(this is a test) 1`] = `1738`; + +exports[`Base64 #encode gas cost of encode(this is a very long string that should cost a lot of gas to encode :)) 1`] = `6083`; + +exports[`Base64 #encode gas cost of encode(😀) 1`] = `1004`; + +exports[`Base64 #encode max size string (24kB) gas cost 1`] = `3731900`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/LiquidityAmounts.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/LiquidityAmounts.spec.ts.snap new file mode 100644 index 0000000..8f420b7 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/LiquidityAmounts.spec.ts.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LiquidityAmounts #getAmount0ForLiquidity gas 1`] = `352`; + +exports[`LiquidityAmounts #getAmountsForLiquidity gas for price above 1`] = `481`; + +exports[`LiquidityAmounts #getAmountsForLiquidity gas for price below 1`] = `502`; + +exports[`LiquidityAmounts #getAmountsForLiquidity gas for price inside 1`] = `840`; + +exports[`LiquidityAmounts #getLiquidityForAmount0 gas 1`] = `565`; + +exports[`LiquidityAmounts #getLiquidityForAmount1 gas 1`] = `362`; + +exports[`LiquidityAmounts #getLiquidityForAmount1 gas 2`] = `368`; + +exports[`LiquidityAmounts #getLiquidityForAmounts gas for price above 1`] = `537`; + +exports[`LiquidityAmounts #getLiquidityForAmounts gas for price below 1`] = `712`; + +exports[`LiquidityAmounts #getLiquidityForAmounts gas for price inside 1`] = `1162`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/Multicall.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/Multicall.spec.ts.snap new file mode 100644 index 0000000..636167d --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/Multicall.spec.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Multicall gas cost of pay w/ multicall 1`] = `45845`; + +exports[`Multicall gas cost of pay w/o multicall 1`] = `43342`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/NFTDescriptor.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/NFTDescriptor.spec.ts.snap new file mode 100644 index 0000000..72b006f --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/NFTDescriptor.spec.ts.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NFTDescriptor #constructTokenURI gas 1`] = `1632898`; + +exports[`NFTDescriptor #constructTokenURI snapshot matches 1`] = `"data:application/json;base64,eyJuYW1lIjoiVW5pc3dhcCAtIDAuMDUlIC0gVU5JL1dFVEggLSAxLjAwMDA8PjEuMTA1MiIsICJkZXNjcmlwdGlvbiI6IlRoaXMgTkZUIHJlcHJlc2VudHMgYSBsaXF1aWRpdHkgcG9zaXRpb24gaW4gYSBVbmlzd2FwIFYzIFVOSS1XRVRIIHBvb2wuIFRoZSBvd25lciBvZiB0aGlzIE5GVCBjYW4gbW9kaWZ5IG9yIHJlZGVlbSB0aGUgcG9zaXRpb24uXG5cblBvb2wgQWRkcmVzczogMHhiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiXG5VTkkgQWRkcmVzczogMHhhYmNkZWFiY2RlZmFiY2RlZmFiY2RlZmFiY2RlZmFiY2RlZmFiY2RmXG5XRVRIIEFkZHJlc3M6IDB4MTIzNDU2Nzg5MDEyMzQ1Njc4OTEyMzQ1Njc4OTAxMjM0NTY3ODkwMVxuRmVlIFRpZXI6IDAuMDUlXG5Ub2tlbiBJRDogMVxuXG7imqDvuI8gRElTQ0xBSU1FUjogRHVlIGRpbGlnZW5jZSBpcyBpbXBlcmF0aXZlIHdoZW4gYXNzZXNzaW5nIHRoaXMgTkZULiBNYWtlIHN1cmUgdG9rZW4gYWRkcmVzc2VzIG1hdGNoIHRoZSBleHBlY3RlZCB0b2tlbnMsIGFzIHRva2VuIHN5bWJvbHMgbWF5IGJlIGltaXRhdGVkLiIsICJpbWFnZSI6ICJkYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LFBITjJaeUIzYVdSMGFEMGlNamt3SWlCb1pXbG5hSFE5SWpVd01DSWdkbWxsZDBKdmVEMGlNQ0F3SURJNU1DQTFNREFpSUhodGJHNXpQU0pvZEhSd09pOHZkM2QzTG5jekxtOXlaeTh5TURBd0wzTjJaeUlnZUcxc2JuTTZlR3hwYm1zOUoyaDBkSEE2THk5M2QzY3Vkek11YjNKbkx6RTVPVGt2ZUd4cGJtc25QanhrWldaelBqeG1hV3gwWlhJZ2FXUTlJbVl4SWo0OFptVkpiV0ZuWlNCeVpYTjFiSFE5SW5Bd0lpQjRiR2x1YXpwb2NtVm1QU0prWVhSaE9tbHRZV2RsTDNOMlp5dDRiV3c3WW1GelpUWTBMRkJJVGpKYWVVSXpZVmRTTUdGRU1HNU5hbXQzU25sQ2IxcFhiRzVoU0ZFNVNucFZkMDFEWTJka2JXeHNaREJLZG1WRU1HNU5RMEYzU1VSSk5VMURRVEZOUkVGdVNVaG9kR0pITlhwUVUyUnZaRWhTZDA5cE9IWmtNMlF6VEc1amVreHRPWGxhZVRoNVRVUkJkMHd6VGpKYWVXTXJVRWhLYkZrelVXZGtNbXhyWkVkbk9VcDZTVFZOU0VJMFNubENiMXBYYkc1aFNGRTVTbnBWZDAxSVFqUktlVUp0WVZkNGMxQlRZMnBaVjBwcVdrZFdhRXA1T0N0UVF6bDZaRzFqS3lJdlBqeG1aVWx0WVdkbElISmxjM1ZzZEQwaWNERWlJSGhzYVc1ck9taHlaV1k5SW1SaGRHRTZhVzFoWjJVdmMzWm5LM2h0YkR0aVlYTmxOalFzVUVoT01scDVRak5oVjFJd1lVUXdiazFxYTNkS2VVSnZXbGRzYm1GSVVUbEtlbFYzVFVOaloyUnRiR3hrTUVwMlpVUXdiazFEUVhkSlJFazFUVU5CTVUxRVFXNUpTR2gwWWtjMWVsQlRaRzlrU0ZKM1QyazRkbVF6WkROTWJtTjZURzA1ZVZwNU9IbE5SRUYzVEROT01scDVZeXRRUjA1d1kyMU9jMXBUUW1wbFJEQnVUV3BaTkVwNVFtcGxWREJ1VFdwVk1VcDVRbmxRVTJONFRXcENkMlZEWTJkYWJXeHpZa1F3YmtsNlJYbE5lbEV4VG1samRsQnFkM1pqTTFwdVVHYzlQU0l2UGp4bVpVbHRZV2RsSUhKbGMzVnNkRDBpY0RJaUlIaHNhVzVyT21oeVpXWTlJbVJoZEdFNmFXMWhaMlV2YzNabkszaHRiRHRpWVhObE5qUXNVRWhPTWxwNVFqTmhWMUl3WVVRd2JrMXFhM2RLZVVKdldsZHNibUZJVVRsS2VsVjNUVU5qWjJSdGJHeGtNRXAyWlVRd2JrMURRWGRKUkVrMVRVTkJNVTFFUVc1SlNHaDBZa2MxZWxCVFpHOWtTRkozVDJrNGRtUXpaRE5NYm1ONlRHMDVlVnA1T0hsTlJFRjNURE5PTWxwNVl5dFFSMDV3WTIxT2MxcFRRbXBsUkRCdVRXcEJNa3A1UW1wbFZEQnVUVlJWZVVwNVFubFFVMk40VFdwQ2QyVkRZMmRhYld4ellrUXdia2t5V21oWmJVNXJXbWxqZGxCcWQzWmpNMXB1VUdjOVBTSWdMejQ4Wm1WSmJXRm5aU0J5WlhOMWJIUTlJbkF6SWlCNGJHbHVhenBvY21WbVBTSmtZWFJoT21sdFlXZGxMM04yWnl0NGJXdzdZbUZ6WlRZMExGQklUakphZVVJellWZFNNR0ZFTUc1TmFtdDNTbmxDYjFwWGJHNWhTRkU1U25wVmQwMURZMmRrYld4c1pEQktkbVZFTUc1TlEwRjNTVVJKTlUxRFFURk5SRUZ1U1Vob2RHSkhOWHBRVTJSdlpFaFNkMDlwT0haa00yUXpURzVqZWt4dE9YbGFlVGg1VFVSQmQwd3pUakphZVdNclVFZE9jR050VG5OYVUwSnFaVVF3YmsxcVVYZEtlVUpxWlZRd2JrMTZRVEpLZVVKNVVGTmplRTFFUW5kbFEyTm5XbTFzYzJKRU1HNUplbGt6VDBScmQwMVRZM1pRYW5kMll6TmFibEJuUFQwaUlDOCtQR1psUW14bGJtUWdiVzlrWlQwaWIzWmxjbXhoZVNJZ2FXNDlJbkF3SWlCcGJqSTlJbkF4SWlBdlBqeG1aVUpzWlc1a0lHMXZaR1U5SW1WNFkyeDFjMmx2YmlJZ2FXNHlQU0p3TWlJZ0x6NDhabVZDYkdWdVpDQnRiMlJsUFNKdmRtVnliR0Y1SWlCcGJqSTlJbkF6SWlCeVpYTjFiSFE5SW1Kc1pXNWtUM1YwSWlBdlBqeG1aVWRoZFhOemFXRnVRbXgxY2lCcGJqMGlZbXhsYm1SUGRYUWlJSE4wWkVSbGRtbGhkR2x2YmowaU5ESWlJQzgrUEM5bWFXeDBaWEkrSUR4amJHbHdVR0YwYUNCcFpEMGlZMjl5Ym1WeWN5SStQSEpsWTNRZ2QybGtkR2c5SWpJNU1DSWdhR1ZwWjJoMFBTSTFNREFpSUhKNFBTSTBNaUlnY25rOUlqUXlJaUF2UGp3dlkyeHBjRkJoZEdnK1BIQmhkR2dnYVdROUluUmxlSFF0Y0dGMGFDMWhJaUJrUFNKTk5EQWdNVElnU0RJMU1DQkJNamdnTWpnZ01DQXdJREVnTWpjNElEUXdJRlkwTmpBZ1FUSTRJREk0SURBZ01DQXhJREkxTUNBME9EZ2dTRFF3SUVFeU9DQXlPQ0F3SURBZ01TQXhNaUEwTmpBZ1ZqUXdJRUV5T0NBeU9DQXdJREFnTVNBME1DQXhNaUI2SWlBdlBqeHdZWFJvSUdsa1BTSnRhVzVwYldGd0lpQmtQU0pOTWpNMElEUTBORU15TXpRZ05EVTNMamswT1NBeU5ESXVNakVnTkRZeklESTFNeUEwTmpNaUlDOCtQR1pwYkhSbGNpQnBaRDBpZEc5d0xYSmxaMmx2YmkxaWJIVnlJajQ4Wm1WSFlYVnpjMmxoYmtKc2RYSWdhVzQ5SWxOdmRYSmpaVWR5WVhCb2FXTWlJSE4wWkVSbGRtbGhkR2x2YmowaU1qUWlJQzgrUEM5bWFXeDBaWEkrUEd4cGJtVmhja2R5WVdScFpXNTBJR2xrUFNKbmNtRmtMWFZ3SWlCNE1UMGlNU0lnZURJOUlqQWlJSGt4UFNJeElpQjVNajBpTUNJK1BITjBiM0FnYjJabWMyVjBQU0l3TGpBaUlITjBiM0F0WTI5c2IzSTlJbmRvYVhSbElpQnpkRzl3TFc5d1lXTnBkSGs5SWpFaUlDOCtQSE4wYjNBZ2IyWm1jMlYwUFNJdU9TSWdjM1J2Y0MxamIyeHZjajBpZDJocGRHVWlJSE4wYjNBdGIzQmhZMmwwZVQwaU1DSWdMejQ4TDJ4cGJtVmhja2R5WVdScFpXNTBQanhzYVc1bFlYSkhjbUZrYVdWdWRDQnBaRDBpWjNKaFpDMWtiM2R1SWlCNE1UMGlNQ0lnZURJOUlqRWlJSGt4UFNJd0lpQjVNajBpTVNJK1BITjBiM0FnYjJabWMyVjBQU0l3TGpBaUlITjBiM0F0WTI5c2IzSTlJbmRvYVhSbElpQnpkRzl3TFc5d1lXTnBkSGs5SWpFaUlDOCtQSE4wYjNBZ2IyWm1jMlYwUFNJd0xqa2lJSE4wYjNBdFkyOXNiM0k5SW5kb2FYUmxJaUJ6ZEc5d0xXOXdZV05wZEhrOUlqQWlJQzgrUEM5c2FXNWxZWEpIY21Ga2FXVnVkRDQ4YldGemF5QnBaRDBpWm1Ga1pTMTFjQ0lnYldGemEwTnZiblJsYm5SVmJtbDBjejBpYjJKcVpXTjBRbTkxYm1ScGJtZENiM2dpUGp4eVpXTjBJSGRwWkhSb1BTSXhJaUJvWldsbmFIUTlJakVpSUdacGJHdzlJblZ5YkNnalozSmhaQzExY0NraUlDOCtQQzl0WVhOclBqeHRZWE5ySUdsa1BTSm1ZV1JsTFdSdmQyNGlJRzFoYzJ0RGIyNTBaVzUwVlc1cGRITTlJbTlpYW1WamRFSnZkVzVrYVc1blFtOTRJajQ4Y21WamRDQjNhV1IwYUQwaU1TSWdhR1ZwWjJoMFBTSXhJaUJtYVd4c1BTSjFjbXdvSTJkeVlXUXRaRzkzYmlraUlDOCtQQzl0WVhOclBqeHRZWE5ySUdsa1BTSnViMjVsSWlCdFlYTnJRMjl1ZEdWdWRGVnVhWFJ6UFNKdlltcGxZM1JDYjNWdVpHbHVaMEp2ZUNJK1BISmxZM1FnZDJsa2RHZzlJakVpSUdobGFXZG9kRDBpTVNJZ1ptbHNiRDBpZDJocGRHVWlJQzgrUEM5dFlYTnJQanhzYVc1bFlYSkhjbUZrYVdWdWRDQnBaRDBpWjNKaFpDMXplVzFpYjJ3aVBqeHpkRzl3SUc5bVpuTmxkRDBpTUM0M0lpQnpkRzl3TFdOdmJHOXlQU0ozYUdsMFpTSWdjM1J2Y0MxdmNHRmphWFI1UFNJeElpQXZQanh6ZEc5d0lHOW1abk5sZEQwaUxqazFJaUJ6ZEc5d0xXTnZiRzl5UFNKM2FHbDBaU0lnYzNSdmNDMXZjR0ZqYVhSNVBTSXdJaUF2UGp3dmJHbHVaV0Z5UjNKaFpHbGxiblErUEcxaGMyc2dhV1E5SW1aaFpHVXRjM2x0WW05c0lpQnRZWE5yUTI5dWRHVnVkRlZ1YVhSelBTSjFjMlZ5VTNCaFkyVlBibFZ6WlNJK1BISmxZM1FnZDJsa2RHZzlJakk1TUhCNElpQm9aV2xuYUhROUlqSXdNSEI0SWlCbWFXeHNQU0oxY213b0kyZHlZV1F0YzNsdFltOXNLU0lnTHo0OEwyMWhjMnMrUEM5a1pXWnpQanhuSUdOc2FYQXRjR0YwYUQwaWRYSnNLQ05qYjNKdVpYSnpLU0krUEhKbFkzUWdabWxzYkQwaVlXSmpaR1ZoSWlCNFBTSXdjSGdpSUhrOUlqQndlQ0lnZDJsa2RHZzlJakk1TUhCNElpQm9aV2xuYUhROUlqVXdNSEI0SWlBdlBqeHlaV04wSUhOMGVXeGxQU0ptYVd4MFpYSTZJSFZ5YkNnalpqRXBJaUI0UFNJd2NIZ2lJSGs5SWpCd2VDSWdkMmxrZEdnOUlqSTVNSEI0SWlCb1pXbG5hSFE5SWpVd01IQjRJaUF2UGlBOFp5QnpkSGxzWlQwaVptbHNkR1Z5T25WeWJDZ2pkRzl3TFhKbFoybHZiaTFpYkhWeUtUc2dkSEpoYm5ObWIzSnRPbk5qWVd4bEtERXVOU2s3SUhSeVlXNXpabTl5YlMxdmNtbG5hVzQ2WTJWdWRHVnlJSFJ2Y0RzaVBqeHlaV04wSUdacGJHdzlJbTV2Ym1VaUlIZzlJakJ3ZUNJZ2VUMGlNSEI0SWlCM2FXUjBhRDBpTWprd2NIZ2lJR2hsYVdkb2REMGlOVEF3Y0hnaUlDOCtQR1ZzYkdsd2MyVWdZM2c5SWpVd0pTSWdZM2s5SWpCd2VDSWdjbmc5SWpFNE1IQjRJaUJ5ZVQwaU1USXdjSGdpSUdacGJHdzlJaU13TURBaUlHOXdZV05wZEhrOUlqQXVPRFVpSUM4K1BDOW5Qanh5WldOMElIZzlJakFpSUhrOUlqQWlJSGRwWkhSb1BTSXlPVEFpSUdobGFXZG9kRDBpTlRBd0lpQnllRDBpTkRJaUlISjVQU0kwTWlJZ1ptbHNiRDBpY21kaVlTZ3dMREFzTUN3d0tTSWdjM1J5YjJ0bFBTSnlaMkpoS0RJMU5Td3lOVFVzTWpVMUxEQXVNaWtpSUM4K1BDOW5QangwWlhoMElIUmxlSFF0Y21WdVpHVnlhVzVuUFNKdmNIUnBiV2w2WlZOd1pXVmtJajQ4ZEdWNGRGQmhkR2dnYzNSaGNuUlBabVp6WlhROUlpMHhNREFsSWlCbWFXeHNQU0ozYUdsMFpTSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NJRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeE1IQjRJaUI0YkdsdWF6cG9jbVZtUFNJamRHVjRkQzF3WVhSb0xXRWlQakI0TVRJek5EVTJOemc1TURFeU16UTFOamM0T1RFeU16UTFOamM0T1RBeE1qTTBOVFkzT0Rrd01TRGlnS0lnVjBWVVNDQThZVzVwYldGMFpTQmhaR1JwZEdsMlpUMGljM1Z0SWlCaGRIUnlhV0oxZEdWT1lXMWxQU0p6ZEdGeWRFOW1abk5sZENJZ1puSnZiVDBpTUNVaUlIUnZQU0l4TURBbElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU16QnpJaUJ5WlhCbFlYUkRiM1Z1ZEQwaWFXNWtaV1pwYm1sMFpTSWdMejQ4TDNSbGVIUlFZWFJvUGlBOGRHVjRkRkJoZEdnZ2MzUmhjblJQWm1aelpYUTlJakFsSWlCbWFXeHNQU0ozYUdsMFpTSWdabTl1ZEMxbVlXMXBiSGs5SWlkRGIzVnlhV1Z5SUU1bGR5Y3NJRzF2Ym05emNHRmpaU0lnWm05dWRDMXphWHBsUFNJeE1IQjRJaUI0YkdsdWF6cG9jbVZtUFNJamRHVjRkQzF3WVhSb0xXRWlQakI0TVRJek5EVTJOemc1TURFeU16UTFOamM0T1RFeU16UTFOamM0T1RBeE1qTTBOVFkzT0Rrd01TRGlnS0lnVjBWVVNDQThZVzVwYldGMFpTQmhaR1JwZEdsMlpUMGljM1Z0SWlCaGRIUnlhV0oxZEdWT1lXMWxQU0p6ZEdGeWRFOW1abk5sZENJZ1puSnZiVDBpTUNVaUlIUnZQU0l4TURBbElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU16QnpJaUJ5WlhCbFlYUkRiM1Z1ZEQwaWFXNWtaV1pwYm1sMFpTSWdMejRnUEM5MFpYaDBVR0YwYUQ0OGRHVjRkRkJoZEdnZ2MzUmhjblJQWm1aelpYUTlJalV3SlNJZ1ptbHNiRDBpZDJocGRHVWlJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MQ0J0YjI1dmMzQmhZMlVpSUdadmJuUXRjMmw2WlQwaU1UQndlQ0lnZUd4cGJtczZhSEpsWmowaUkzUmxlSFF0Y0dGMGFDMWhJajR3ZUdGaVkyUmxZV0pqWkdWbVlXSmpaR1ZtWVdKalpHVm1ZV0pqWkdWbVlXSmpaR1ZtWVdKalpHWWc0b0NpSUZWT1NTQThZVzVwYldGMFpTQmhaR1JwZEdsMlpUMGljM1Z0SWlCaGRIUnlhV0oxZEdWT1lXMWxQU0p6ZEdGeWRFOW1abk5sZENJZ1puSnZiVDBpTUNVaUlIUnZQU0l4TURBbElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU16QnpJaUJ5WlhCbFlYUkRiM1Z1ZEQwaWFXNWtaV1pwYm1sMFpTSWdMejQ4TDNSbGVIUlFZWFJvUGp4MFpYaDBVR0YwYUNCemRHRnlkRTltWm5ObGREMGlMVFV3SlNJZ1ptbHNiRDBpZDJocGRHVWlJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MQ0J0YjI1dmMzQmhZMlVpSUdadmJuUXRjMmw2WlQwaU1UQndlQ0lnZUd4cGJtczZhSEpsWmowaUkzUmxlSFF0Y0dGMGFDMWhJajR3ZUdGaVkyUmxZV0pqWkdWbVlXSmpaR1ZtWVdKalpHVm1ZV0pqWkdWbVlXSmpaR1ZtWVdKalpHWWc0b0NpSUZWT1NTQThZVzVwYldGMFpTQmhaR1JwZEdsMlpUMGljM1Z0SWlCaGRIUnlhV0oxZEdWT1lXMWxQU0p6ZEdGeWRFOW1abk5sZENJZ1puSnZiVDBpTUNVaUlIUnZQU0l4TURBbElpQmlaV2RwYmowaU1ITWlJR1IxY2owaU16QnpJaUJ5WlhCbFlYUkRiM1Z1ZEQwaWFXNWtaV1pwYm1sMFpTSWdMejQ4TDNSbGVIUlFZWFJvUGp3dmRHVjRkRDQ4WnlCdFlYTnJQU0oxY213b0kyWmhaR1V0YzNsdFltOXNLU0krUEhKbFkzUWdabWxzYkQwaWJtOXVaU0lnZUQwaU1IQjRJaUI1UFNJd2NIZ2lJSGRwWkhSb1BTSXlPVEJ3ZUNJZ2FHVnBaMmgwUFNJeU1EQndlQ0lnTHo0Z1BIUmxlSFFnZVQwaU56QndlQ0lnZUQwaU16SndlQ0lnWm1sc2JEMGlkMmhwZEdVaUlHWnZiblF0Wm1GdGFXeDVQU0luUTI5MWNtbGxjaUJPWlhjbkxDQnRiMjV2YzNCaFkyVWlJR1p2Ym5RdGQyVnBaMmgwUFNJeU1EQWlJR1p2Ym5RdGMybDZaVDBpTXpad2VDSStWVTVKTDFkRlZFZzhMM1JsZUhRK1BIUmxlSFFnZVQwaU1URTFjSGdpSUhnOUlqTXljSGdpSUdacGJHdzlJbmRvYVhSbElpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5d2diVzl1YjNOd1lXTmxJaUJtYjI1MExYZGxhV2RvZEQwaU1qQXdJaUJtYjI1MExYTnBlbVU5SWpNMmNIZ2lQakF1TURVbFBDOTBaWGgwUGp3dlp6NDhjbVZqZENCNFBTSXhOaUlnZVQwaU1UWWlJSGRwWkhSb1BTSXlOVGdpSUdobGFXZG9kRDBpTkRZNElpQnllRDBpTWpZaUlISjVQU0l5TmlJZ1ptbHNiRDBpY21kaVlTZ3dMREFzTUN3d0tTSWdjM1J5YjJ0bFBTSnlaMkpoS0RJMU5Td3lOVFVzTWpVMUxEQXVNaWtpSUM4K1BHY2diV0Z6YXowaWRYSnNLQ05tWVdSbExXUnZkMjRwSWlCemRIbHNaVDBpZEhKaGJuTm1iM0p0T25SeVlXNXpiR0YwWlNnM01uQjRMREU0T1hCNEtTSStQSEpsWTNRZ2VEMGlMVEUyY0hnaUlIazlJaTB4Tm5CNElpQjNhV1IwYUQwaU1UZ3djSGdpSUdobGFXZG9kRDBpTVRnd2NIZ2lJR1pwYkd3OUltNXZibVVpSUM4K1BIQmhkR2dnWkQwaVRURWdNVU01SURneElEWTFJREV6TnlBeE5EVWdNVFExSWlCemRISnZhMlU5SW5KblltRW9NQ3d3TERBc01DNHpLU0lnYzNSeWIydGxMWGRwWkhSb1BTSXpNbkI0SWlCbWFXeHNQU0p1YjI1bElpQnpkSEp2YTJVdGJHbHVaV05oY0QwaWNtOTFibVFpSUM4K1BDOW5QanhuSUcxaGMyczlJblZ5YkNnalptRmtaUzFrYjNkdUtTSWdjM1I1YkdVOUluUnlZVzV6Wm05eWJUcDBjbUZ1YzJ4aGRHVW9Oekp3ZUN3eE9EbHdlQ2tpUGp4eVpXTjBJSGc5SWkweE5uQjRJaUI1UFNJdE1UWndlQ0lnZDJsa2RHZzlJakU0TUhCNElpQm9aV2xuYUhROUlqRTRNSEI0SWlCbWFXeHNQU0p1YjI1bElpQXZQanh3WVhSb0lHUTlJazB4SURGRE9TQTRNU0EyTlNBeE16Y2dNVFExSURFME5TSWdjM1J5YjJ0bFBTSnlaMkpoS0RJMU5Td3lOVFVzTWpVMUxERXBJaUJtYVd4c1BTSnViMjVsSWlCemRISnZhMlV0YkdsdVpXTmhjRDBpY205MWJtUWlJQzgrUEM5blBqeGphWEpqYkdVZ1kzZzlJamN6Y0hnaUlHTjVQU0l4T1RCd2VDSWdjajBpTkhCNElpQm1hV3hzUFNKM2FHbDBaU0lnTHo0OFkybHlZMnhsSUdONFBTSTNNM0I0SWlCamVUMGlNVGt3Y0hnaUlISTlJakkwY0hnaUlHWnBiR3c5SW01dmJtVWlJSE4wY205clpUMGlkMmhwZEdVaUlDOCtJRHhuSUhOMGVXeGxQU0owY21GdWMyWnZjbTA2ZEhKaGJuTnNZWFJsS0RJNWNIZ3NJRE00TkhCNEtTSStQSEpsWTNRZ2QybGtkR2c5SWpZemNIZ2lJR2hsYVdkb2REMGlNalp3ZUNJZ2NuZzlJamh3ZUNJZ2NuazlJamh3ZUNJZ1ptbHNiRDBpY21kaVlTZ3dMREFzTUN3d0xqWXBJaUF2UGp4MFpYaDBJSGc5SWpFeWNIZ2lJSGs5SWpFM2NIZ2lJR1p2Ym5RdFptRnRhV3g1UFNJblEyOTFjbWxsY2lCT1pYY25MQ0J0YjI1dmMzQmhZMlVpSUdadmJuUXRjMmw2WlQwaU1USndlQ0lnWm1sc2JEMGlkMmhwZEdVaVBqeDBjM0JoYmlCbWFXeHNQU0p5WjJKaEtESTFOU3d5TlRVc01qVTFMREF1TmlraVBrbEVPaUE4TDNSemNHRnVQakU4TDNSbGVIUStQQzluUGlBOFp5QnpkSGxzWlQwaWRISmhibk5tYjNKdE9uUnlZVzV6YkdGMFpTZ3lPWEI0TENBME1UUndlQ2tpUGp4eVpXTjBJSGRwWkhSb1BTSXhNRFZ3ZUNJZ2FHVnBaMmgwUFNJeU5uQjRJaUJ5ZUQwaU9IQjRJaUJ5ZVQwaU9IQjRJaUJtYVd4c1BTSnlaMkpoS0RBc01Dd3dMREF1TmlraUlDOCtQSFJsZUhRZ2VEMGlNVEp3ZUNJZ2VUMGlNVGR3ZUNJZ1ptOXVkQzFtWVcxcGJIazlJaWREYjNWeWFXVnlJRTVsZHljc0lHMXZibTl6Y0dGalpTSWdabTl1ZEMxemFYcGxQU0l4TW5CNElpQm1hV3hzUFNKM2FHbDBaU0krUEhSemNHRnVJR1pwYkd3OUluSm5ZbUVvTWpVMUxESTFOU3d5TlRVc01DNDJLU0krVFdsdUlGUnBZMnM2SUR3dmRITndZVzQrTUR3dmRHVjRkRDQ4TDJjK0lEeG5JSE4wZVd4bFBTSjBjbUZ1YzJadmNtMDZkSEpoYm5Oc1lYUmxLREk1Y0hnc0lEUTBOSEI0S1NJK1BISmxZM1FnZDJsa2RHZzlJakV5Tm5CNElpQm9aV2xuYUhROUlqSTJjSGdpSUhKNFBTSTRjSGdpSUhKNVBTSTRjSGdpSUdacGJHdzlJbkpuWW1Fb01Dd3dMREFzTUM0MktTSWdMejQ4ZEdWNGRDQjRQU0l4TW5CNElpQjVQU0l4TjNCNElpQm1iMjUwTFdaaGJXbHNlVDBpSjBOdmRYSnBaWElnVG1WM0p5d2diVzl1YjNOd1lXTmxJaUJtYjI1MExYTnBlbVU5SWpFeWNIZ2lJR1pwYkd3OUluZG9hWFJsSWo0OGRITndZVzRnWm1sc2JEMGljbWRpWVNneU5UVXNNalUxTERJMU5Td3dMallwSWo1TllYZ2dWR2xqYXpvZ1BDOTBjM0JoYmo0eE1EQXdQQzkwWlhoMFBqd3ZaejQ4WnlCemRIbHNaVDBpZEhKaGJuTm1iM0p0T25SeVlXNXpiR0YwWlNneU1qWndlQ3dnTkRNemNIZ3BJajQ4Y21WamRDQjNhV1IwYUQwaU16WndlQ0lnYUdWcFoyaDBQU0l6Tm5CNElpQnllRDBpT0hCNElpQnllVDBpT0hCNElpQm1hV3hzUFNKdWIyNWxJaUJ6ZEhKdmEyVTlJbkpuWW1Fb01qVTFMREkxTlN3eU5UVXNNQzR5S1NJZ0x6NDhjR0YwYUNCemRISnZhMlV0YkdsdVpXTmhjRDBpY205MWJtUWlJR1E5SWswNElEbERPQzR3TURBd05DQXlNaTQ1TkRrMElERTJMakl3T1RrZ01qZ2dNamNnTWpnaUlHWnBiR3c5SW01dmJtVWlJSE4wY205clpUMGlkMmhwZEdVaUlDOCtQR05wY21Oc1pTQnpkSGxzWlQwaWRISmhibk5tYjNKdE9uUnlZVzV6YkdGMFpUTmtLREV6Y0hnc0lESXpjSGdzSURCd2VDa2lJR040UFNJd2NIZ2lJR041UFNJd2NIZ2lJSEk5SWpSd2VDSWdabWxzYkQwaWQyaHBkR1VpTHo0OEwyYytQR2NnYzNSNWJHVTlJblJ5WVc1elptOXliVHAwY21GdWMyeGhkR1VvTWpJMmNIZ3NJRE01TW5CNEtTSStQSEpsWTNRZ2QybGtkR2c5SWpNMmNIZ2lJR2hsYVdkb2REMGlNelp3ZUNJZ2NuZzlJamh3ZUNJZ2NuazlJamh3ZUNJZ1ptbHNiRDBpYm05dVpTSWdjM1J5YjJ0bFBTSnlaMkpoS0RJMU5Td3lOVFVzTWpVMUxEQXVNaWtpSUM4K1BHYytQSEJoZEdnZ2MzUjViR1U5SW5SeVlXNXpabTl5YlRwMGNtRnVjMnhoZEdVb05uQjRMRFp3ZUNraUlHUTlJazB4TWlBd1RERXlMalkxTWpJZ09TNDFOalU0TjB3eE9DQXhMall3TnpkTU1UTXVOemd4T1NBeE1DNHlNVGd4VERJeUxqTTVNak1nTmt3eE5DNDBNelF4SURFeExqTTBOemhNTWpRZ01USk1NVFF1TkRNME1TQXhNaTQyTlRJeVRESXlMak01TWpNZ01UaE1NVE11TnpneE9TQXhNeTQzT0RFNVRERTRJREl5TGpNNU1qTk1NVEl1TmpVeU1pQXhOQzQwTXpReFRERXlJREkwVERFeExqTTBOemdnTVRRdU5ETTBNVXcySURJeUxqTTVNak5NTVRBdU1qRTRNU0F4TXk0M09ERTVUREV1TmpBM055QXhPRXc1TGpVMk5UZzNJREV5TGpZMU1qSk1NQ0F4TWt3NUxqVTJOVGczSURFeExqTTBOemhNTVM0Mk1EYzNJRFpNTVRBdU1qRTRNU0F4TUM0eU1UZ3hURFlnTVM0Mk1EYzNUREV4TGpNME56Z2dPUzQxTmpVNE4wd3hNaUF3V2lJZ1ptbHNiRDBpZDJocGRHVWlJQzgrUEdGdWFXMWhkR1ZVY21GdWMyWnZjbTBnWVhSMGNtbGlkWFJsVG1GdFpUMGlkSEpoYm5ObWIzSnRJaUIwZVhCbFBTSnliM1JoZEdVaUlHWnliMjA5SWpBZ01UZ2dNVGdpSUhSdlBTSXpOakFnTVRnZ01UZ2lJR1IxY2owaU1UQnpJaUJ5WlhCbFlYUkRiM1Z1ZEQwaWFXNWtaV1pwYm1sMFpTSXZQand2Wno0OEwyYytQQzl6ZG1jKyJ9"`; + +exports[`NFTDescriptor #svgImage matches the current snapshot 1`] = `" 0xabcdeabcdefabcdefabcdefabcdefabcdefabcdf • WETH 0xabcdeabcdefabcdefabcdefabcdefabcdefabcdf • WETH 0x1234567890123456789123456789012345678901 • UNI 0x1234567890123456789123456789012345678901 • UNI UNI/WETH0.05% ID: 123 Min Tick: -1000 Max Tick: 2000"`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/NFTDescriptor.svg b/lib/uniswap/v3-periphery/test/__snapshots__/NFTDescriptor.svg new file mode 100644 index 0000000..d09c263 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/NFTDescriptor.svg @@ -0,0 +1 @@ + 0xabcdeabcdefabcdefabcdefabcdefabcdefabcdf • WETH 0xabcdeabcdefabcdefabcdefabcdefabcdefabcdf • WETH 0x1234567890123456789123456789012345678901 • UNI 0x1234567890123456789123456789012345678901 • UNI UNI/WETH0.05% ID: 123 Min Tick: -1000 Max Tick: 2000 \ No newline at end of file diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap new file mode 100644 index 0000000..488d514 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/NonfungiblePositionManager.spec.ts.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`NonfungiblePositionManager #burn gas 1`] = `66076`; + +exports[`NonfungiblePositionManager #collect gas transfers both 1`] = `118970`; + +exports[`NonfungiblePositionManager #collect gas transfers token0 only 1`] = `111897`; + +exports[`NonfungiblePositionManager #collect gas transfers token1 only 1`] = `112104`; + +exports[`NonfungiblePositionManager #createAndInitializePoolIfNecessary gas 1`] = `4612043`; + +exports[`NonfungiblePositionManager #decreaseLiquidity gas complete decrease 1`] = `148666`; + +exports[`NonfungiblePositionManager #decreaseLiquidity gas partial decrease 1`] = `161366`; + +exports[`NonfungiblePositionManager #increaseLiquidity gas 1`] = `176626`; + +exports[`NonfungiblePositionManager #mint gas first mint for pool 1`] = `619406`; + +exports[`NonfungiblePositionManager #mint gas first mint for pool using eth with non-zero refund 1`] = `629749`; + +exports[`NonfungiblePositionManager #mint gas first mint for pool using eth with zero refund 1`] = `622524`; + +exports[`NonfungiblePositionManager #mint gas mint for same pool, different ticks 1`] = `451862`; + +exports[`NonfungiblePositionManager #mint gas mint on same ticks 1`] = `344732`; + +exports[`NonfungiblePositionManager #permit owned by eoa gas 1`] = `64492`; + +exports[`NonfungiblePositionManager #permit owned by verifying contract gas 1`] = `68289`; + +exports[`NonfungiblePositionManager #positions gas 1`] = `19890`; + +exports[`NonfungiblePositionManager #transferFrom gas 1`] = `109691`; + +exports[`NonfungiblePositionManager #transferFrom gas comes from approved 1`] = `108291`; + +exports[`NonfungiblePositionManager bytecode size 1`] = `24450`; + +exports[`NonfungiblePositionManager multicall exit gas 1`] = `228718`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/OracleLibrary.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/OracleLibrary.spec.ts.snap new file mode 100644 index 0000000..e8fd682 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/OracleLibrary.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OracleLibrary #getQuoteAtTick gas test 1`] = `1205`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/PairFlash.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/PairFlash.spec.ts.snap new file mode 100644 index 0000000..558e03e --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/PairFlash.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PairFlash test flash gas 1`] = `349643`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/Path.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/Path.spec.ts.snap new file mode 100644 index 0000000..e450df9 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/Path.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Path gas cost 1`] = `451`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/PeripheryImmutableState.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/PeripheryImmutableState.spec.ts.snap new file mode 100644 index 0000000..b79d75f --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/PeripheryImmutableState.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PeripheryImmutableState bytecode size 1`] = `193`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/PoolAddress.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/PoolAddress.spec.ts.snap new file mode 100644 index 0000000..7471415 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/PoolAddress.spec.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PoolAddress #computeAddress gas cost 1`] = `642`; + +exports[`PoolAddress #computeAddress matches example from core repo 1`] = `"0x03D8bab195A5BC23d249693F53dfA0e358F2650D"`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/PositionValue.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/PositionValue.spec.ts.snap new file mode 100644 index 0000000..3d8f9d4 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/PositionValue.spec.ts.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PositionValue #fees when price is above the position range gas 1`] = `47658`; + +exports[`PositionValue #fees when price is below the position range gas 1`] = `47626`; + +exports[`PositionValue #fees when price is within the position range gas 1`] = `53216`; + +exports[`PositionValue #principal gas 1`] = `23001`; + +exports[`PositionValue #total gas 1`] = `59777`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/QuoterV2.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/QuoterV2.spec.ts.snap new file mode 100644 index 0000000..ccb9341 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/QuoterV2.spec.ts.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`QuoterV2 quotes #quoteExactInput 0 -> 2 -> 1 1`] = `277146`; + +exports[`QuoterV2 quotes #quoteExactInput 0 -> 2 cross 0 tick, starting tick initialized 1`] = `123797`; + +exports[`QuoterV2 quotes #quoteExactInput 0 -> 2 cross 0 tick, starting tick not initialized 1`] = `100962`; + +exports[`QuoterV2 quotes #quoteExactInput 0 -> 2 cross 1 tick 1`] = `144724`; + +exports[`QuoterV2 quotes #quoteExactInput 0 -> 2 cross 2 tick 1`] = `182321`; + +exports[`QuoterV2 quotes #quoteExactInput 0 -> 2 cross 2 tick where after is initialized 1`] = `144762`; + +exports[`QuoterV2 quotes #quoteExactInput 2 -> 0 cross 0 tick, starting tick initialized 1`] = `97654`; + +exports[`QuoterV2 quotes #quoteExactInput 2 -> 0 cross 0 tick, starting tick not initialized 1`] = `93779`; + +exports[`QuoterV2 quotes #quoteExactInput 2 -> 0 cross 2 1`] = `175133`; + +exports[`QuoterV2 quotes #quoteExactInput 2 -> 0 cross 2 where tick after is initialized 1`] = `175141`; + +exports[`QuoterV2 quotes #quoteExactInput 2 -> 1 1`] = `97329`; + +exports[`QuoterV2 quotes #quoteExactInputSingle 0 -> 2 1`] = `182303`; + +exports[`QuoterV2 quotes #quoteExactInputSingle 2 -> 0 1`] = `175105`; + +exports[`QuoterV2 quotes #quoteExactOutput 0 -> 2 -> 1 1`] = `276746`; + +exports[`QuoterV2 quotes #quoteExactOutput 0 -> 2 cross 0 tick starting tick initialized 1`] = `123352`; + +exports[`QuoterV2 quotes #quoteExactOutput 0 -> 2 cross 0 tick starting tick not initialized 1`] = `100537`; + +exports[`QuoterV2 quotes #quoteExactOutput 0 -> 2 cross 1 tick 1`] = `144010`; + +exports[`QuoterV2 quotes #quoteExactOutput 0 -> 2 cross 2 tick 1`] = `181363`; + +exports[`QuoterV2 quotes #quoteExactOutput 0 -> 2 cross 2 where tick after is initialized 1`] = `144048`; + +exports[`QuoterV2 quotes #quoteExactOutput 2 -> 0 cross 1 tick 1`] = `137858`; + +exports[`QuoterV2 quotes #quoteExactOutput 2 -> 0 cross 2 ticks 1`] = `175203`; + +exports[`QuoterV2 quotes #quoteExactOutput 2 -> 0 cross 2 where tick after is initialized 1`] = `175197`; + +exports[`QuoterV2 quotes #quoteExactOutput 2 -> 1 1`] = `97870`; + +exports[`QuoterV2 quotes #quoteExactOutputSingle 0 -> 1 1`] = `105200`; + +exports[`QuoterV2 quotes #quoteExactOutputSingle 1 -> 0 1`] = `98511`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/SwapRouter.gas.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/SwapRouter.gas.spec.ts.snap new file mode 100644 index 0000000..ba9a0a9 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/SwapRouter.gas.spec.ts.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SwapRouter gas tests #exactInput 0 -> 1 -> 2 1`] = `172498`; + +exports[`SwapRouter gas tests #exactInput 0 -> 1 1`] = `107759`; + +exports[`SwapRouter gas tests #exactInput 0 -> 1 minimal 1`] = `98059`; + +exports[`SwapRouter gas tests #exactInput 0 -> WETH9 1`] = `127578`; + +exports[`SwapRouter gas tests #exactInput 2 trades (via router) 1`] = `188814`; + +exports[`SwapRouter gas tests #exactInput 3 trades (directly to sender) 1`] = `258601`; + +exports[`SwapRouter gas tests #exactInput WETH9 -> 0 1`] = `106083`; + +exports[`SwapRouter gas tests #exactInputSingle 0 -> 1 1`] = `107180`; + +exports[`SwapRouter gas tests #exactInputSingle 0 -> WETH9 1`] = `126993`; + +exports[`SwapRouter gas tests #exactInputSingle WETH9 -> 0 1`] = `105504`; + +exports[`SwapRouter gas tests #exactOutput 0 -> 1 -> 2 1`] = `169275`; + +exports[`SwapRouter gas tests #exactOutput 0 -> 1 1`] = `111757`; + +exports[`SwapRouter gas tests #exactOutput 0 -> WETH9 1`] = `128821`; + +exports[`SwapRouter gas tests #exactOutput WETH9 -> 0 1`] = `119691`; + +exports[`SwapRouter gas tests #exactOutputSingle 0 -> 1 1`] = `111967`; + +exports[`SwapRouter gas tests #exactOutputSingle 0 -> WETH9 1`] = `129031`; + +exports[`SwapRouter gas tests #exactOutputSingle WETH9 -> 0 1`] = `114366`; + +exports[`SwapRouter gas tests 3 trades (directly to sender) 1`] = `179440`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/SwapRouter.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/SwapRouter.spec.ts.snap new file mode 100644 index 0000000..d123c7d --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/SwapRouter.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SwapRouter bytecode size 1`] = `12160`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/TickLens.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/TickLens.spec.ts.snap new file mode 100644 index 0000000..3bdf4fc --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/TickLens.spec.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TickLens #getPopulatedTicksInWord fully populated ticks 1`] = `2803889`; + +exports[`TickLens #getPopulatedTicksInWord gas for single populated tick 1`] = `55664`; diff --git a/lib/uniswap/v3-periphery/test/__snapshots__/V3Migrator.spec.ts.snap b/lib/uniswap/v3-periphery/test/__snapshots__/V3Migrator.spec.ts.snap new file mode 100644 index 0000000..0248aa8 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/__snapshots__/V3Migrator.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`V3Migrator #migrate gas 1`] = `730625`; diff --git a/lib/uniswap/v3-periphery/test/contracts/WETH9.json b/lib/uniswap/v3-periphery/test/contracts/WETH9.json new file mode 100644 index 0000000..68ec24f --- /dev/null +++ b/lib/uniswap/v3-periphery/test/contracts/WETH9.json @@ -0,0 +1,156 @@ +{ + "bytecode": "60606040526040805190810160405280600d81526020017f57726170706564204574686572000000000000000000000000000000000000008152506000908051906020019061004f9291906100c8565b506040805190810160405280600481526020017f57455448000000000000000000000000000000000000000000000000000000008152506001908051906020019061009b9291906100c8565b506012600260006101000a81548160ff021916908360ff16021790555034156100c357600080fd5b61016d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061010957805160ff1916838001178555610137565b82800160010185558215610137579182015b8281111561013657825182559160200191906001019061011b565b5b5090506101449190610148565b5090565b61016a91905b8082111561016657600081600090555060010161014e565b5090565b90565b610c348061017c6000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029", + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "guy", "type": "address" }, + { "name": "wad", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "src", "type": "address" }, + { "name": "dst", "type": "address" }, + { "name": "wad", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "wad", "type": "uint256" }], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [{ "name": "", "type": "uint8" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "dst", "type": "address" }, + { "name": "wad", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "", "type": "address" }, + { "name": "", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { "payable": true, "stateMutability": "payable", "type": "fallback" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "src", "type": "address" }, + { "indexed": true, "name": "guy", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "src", "type": "address" }, + { "indexed": true, "name": "dst", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "dst", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "src", "type": "address" }, + { "indexed": false, "name": "wad", "type": "uint256" } + ], + "name": "Withdrawal", + "type": "event" + } + ] +} diff --git a/lib/uniswap/v3-periphery/test/shared/base64.ts b/lib/uniswap/v3-periphery/test/shared/base64.ts new file mode 100644 index 0000000..3a17c2f --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/base64.ts @@ -0,0 +1,7 @@ +export function base64Encode(str: string): string { + return Buffer.from(str, 'utf8').toString('base64') +} + +export function base64Decode(str: string): string { + return Buffer.from(str, 'base64').toString('utf8') +} diff --git a/lib/uniswap/v3-periphery/test/shared/completeFixture.ts b/lib/uniswap/v3-periphery/test/shared/completeFixture.ts new file mode 100644 index 0000000..6f3a8e9 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/completeFixture.ts @@ -0,0 +1,63 @@ +import { Fixture } from 'ethereum-waffle' +import { ethers } from 'hardhat' +import { v3RouterFixture } from './externalFixtures' +import { constants } from 'ethers' +import { + IWETH9, + MockTimeNonfungiblePositionManager, + MockTimeSwapRouter, + NonfungibleTokenPositionDescriptor, + TestERC20, + IUniswapV3Factory, +} from '../../typechain' + +const completeFixture: Fixture<{ + weth9: IWETH9 + factory: IUniswapV3Factory + router: MockTimeSwapRouter + nft: MockTimeNonfungiblePositionManager + nftDescriptor: NonfungibleTokenPositionDescriptor + tokens: [TestERC20, TestERC20, TestERC20] +}> = async ([wallet], provider) => { + const { weth9, factory, router } = await v3RouterFixture([wallet], provider) + + const tokenFactory = await ethers.getContractFactory('TestERC20') + const tokens: [TestERC20, TestERC20, TestERC20] = [ + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, // do not use maxu256 to avoid overflowing + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + (await tokenFactory.deploy(constants.MaxUint256.div(2))) as TestERC20, + ] + + const nftDescriptorLibraryFactory = await ethers.getContractFactory('NFTDescriptor') + const nftDescriptorLibrary = await nftDescriptorLibraryFactory.deploy() + const positionDescriptorFactory = await ethers.getContractFactory('NonfungibleTokenPositionDescriptor', { + libraries: { + NFTDescriptor: nftDescriptorLibrary.address, + }, + }) + const nftDescriptor = (await positionDescriptorFactory.deploy( + tokens[0].address, + // 'ETH' as a bytes32 string + '0x4554480000000000000000000000000000000000000000000000000000000000' + )) as NonfungibleTokenPositionDescriptor + + const positionManagerFactory = await ethers.getContractFactory('MockTimeNonfungiblePositionManager') + const nft = (await positionManagerFactory.deploy( + factory.address, + weth9.address, + nftDescriptor.address + )) as MockTimeNonfungiblePositionManager + + tokens.sort((a, b) => (a.address.toLowerCase() < b.address.toLowerCase() ? -1 : 1)) + + return { + weth9, + factory, + router, + tokens, + nft, + nftDescriptor, + } +} + +export default completeFixture diff --git a/lib/uniswap/v3-periphery/test/shared/computePoolAddress.ts b/lib/uniswap/v3-periphery/test/shared/computePoolAddress.ts new file mode 100644 index 0000000..fde8c8a --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/computePoolAddress.ts @@ -0,0 +1,22 @@ +import { bytecode } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json' +import { utils } from 'ethers' + +export const POOL_BYTECODE_HASH = utils.keccak256(bytecode) + +export function computePoolAddress(factoryAddress: string, [tokenA, tokenB]: [string, string], fee: number): string { + const [token0, token1] = tokenA.toLowerCase() < tokenB.toLowerCase() ? [tokenA, tokenB] : [tokenB, tokenA] + const constructorArgumentsEncoded = utils.defaultAbiCoder.encode( + ['address', 'address', 'uint24'], + [token0, token1, fee] + ) + const create2Inputs = [ + '0xff', + factoryAddress, + // salt + utils.keccak256(constructorArgumentsEncoded), + // init code hash + POOL_BYTECODE_HASH, + ] + const sanitizedInputs = `0x${create2Inputs.map((i) => i.slice(2)).join('')}` + return utils.getAddress(`0x${utils.keccak256(sanitizedInputs).slice(-40)}`) +} diff --git a/lib/uniswap/v3-periphery/test/shared/constants.ts b/lib/uniswap/v3-periphery/test/shared/constants.ts new file mode 100644 index 0000000..df044c9 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/constants.ts @@ -0,0 +1,15 @@ +import { BigNumber } from 'ethers' + +export const MaxUint128 = BigNumber.from(2).pow(128).sub(1) + +export enum FeeAmount { + LOW = 500, + MEDIUM = 3000, + HIGH = 10000, +} + +export const TICK_SPACINGS: { [amount in FeeAmount]: number } = { + [FeeAmount.LOW]: 10, + [FeeAmount.MEDIUM]: 60, + [FeeAmount.HIGH]: 200, +} diff --git a/lib/uniswap/v3-periphery/test/shared/encodePriceSqrt.ts b/lib/uniswap/v3-periphery/test/shared/encodePriceSqrt.ts new file mode 100644 index 0000000..f95cd23 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/encodePriceSqrt.ts @@ -0,0 +1,16 @@ +import bn from 'bignumber.js' +import { BigNumber, BigNumberish } from 'ethers' + +bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 }) + +// returns the sqrt price as a 64x96 +export function encodePriceSqrt(reserve1: BigNumberish, reserve0: BigNumberish): BigNumber { + return BigNumber.from( + new bn(reserve1.toString()) + .div(reserve0.toString()) + .sqrt() + .multipliedBy(new bn(2).pow(96)) + .integerValue(3) + .toString() + ) +} diff --git a/lib/uniswap/v3-periphery/test/shared/expandTo18Decimals.ts b/lib/uniswap/v3-periphery/test/shared/expandTo18Decimals.ts new file mode 100644 index 0000000..30d2864 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/expandTo18Decimals.ts @@ -0,0 +1,5 @@ +import { BigNumber } from 'ethers' + +export function expandTo18Decimals(n: number): BigNumber { + return BigNumber.from(n).mul(BigNumber.from(10).pow(18)) +} diff --git a/lib/uniswap/v3-periphery/test/shared/expect.ts b/lib/uniswap/v3-periphery/test/shared/expect.ts new file mode 100644 index 0000000..ac8f19b --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/expect.ts @@ -0,0 +1,8 @@ +import { expect, use } from 'chai' +import { solidity } from 'ethereum-waffle' +import { jestSnapshotPlugin } from 'mocha-chai-jest-snapshot' + +use(solidity) +use(jestSnapshotPlugin()) + +export { expect } diff --git a/lib/uniswap/v3-periphery/test/shared/externalFixtures.ts b/lib/uniswap/v3-periphery/test/shared/externalFixtures.ts new file mode 100644 index 0000000..4ac7603 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/externalFixtures.ts @@ -0,0 +1,57 @@ +import { + abi as FACTORY_ABI, + bytecode as FACTORY_BYTECODE, +} from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' +import { abi as FACTORY_V2_ABI, bytecode as FACTORY_V2_BYTECODE } from '@uniswap/v2-core/build/UniswapV2Factory.json' +import { Fixture } from 'ethereum-waffle' +import { ethers, waffle } from 'hardhat' +import { IUniswapV3Factory, IWETH9, MockTimeSwapRouter } from '../../typechain' + +import WETH9 from '../contracts/WETH9.json' +import { Contract } from '@ethersproject/contracts' +import { constants } from 'ethers' + +const wethFixture: Fixture<{ weth9: IWETH9 }> = async ([wallet]) => { + const weth9 = (await waffle.deployContract(wallet, { + bytecode: WETH9.bytecode, + abi: WETH9.abi, + })) as IWETH9 + + return { weth9 } +} + +export const v2FactoryFixture: Fixture<{ factory: Contract }> = async ([wallet]) => { + const factory = await waffle.deployContract( + wallet, + { + bytecode: FACTORY_V2_BYTECODE, + abi: FACTORY_V2_ABI, + }, + [constants.AddressZero] + ) + + return { factory } +} + +const v3CoreFactoryFixture: Fixture = async ([wallet]) => { + return (await waffle.deployContract(wallet, { + bytecode: FACTORY_BYTECODE, + abi: FACTORY_ABI, + })) as IUniswapV3Factory +} + +export const v3RouterFixture: Fixture<{ + weth9: IWETH9 + factory: IUniswapV3Factory + router: MockTimeSwapRouter +}> = async ([wallet], provider) => { + const { weth9 } = await wethFixture([wallet], provider) + const factory = await v3CoreFactoryFixture([wallet], provider) + + const router = (await (await ethers.getContractFactory('MockTimeSwapRouter')).deploy( + factory.address, + weth9.address + )) as MockTimeSwapRouter + + return { factory, weth9, router } +} diff --git a/lib/uniswap/v3-periphery/test/shared/extractJSONFromURI.ts b/lib/uniswap/v3-periphery/test/shared/extractJSONFromURI.ts new file mode 100644 index 0000000..47105be --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/extractJSONFromURI.ts @@ -0,0 +1,5 @@ +export function extractJSONFromURI(uri: string): { name: string; description: string; image: string } { + const encodedJSON = uri.substr('data:application/json;base64,'.length) + const decodedJSON = Buffer.from(encodedJSON, 'base64').toString('utf8') + return JSON.parse(decodedJSON) +} diff --git a/lib/uniswap/v3-periphery/test/shared/formatSqrtRatioX96.spec.ts b/lib/uniswap/v3-periphery/test/shared/formatSqrtRatioX96.spec.ts new file mode 100644 index 0000000..61e9661 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/formatSqrtRatioX96.spec.ts @@ -0,0 +1,30 @@ +import { encodePriceSqrt } from './encodePriceSqrt' +import { expect } from './expect' +import { formatSqrtRatioX96 } from './formatSqrtRatioX96' + +describe('#formatSqrtRatioX96', () => { + it('is correct for 9_999_999/10_000_000', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(9_999_999, 10_000_000))).to.eq('1.0000') + }) + it('is correct for 9_999_999/1', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(9_999_999, 1))).to.eq('10000000') + }) + it('is correct for 1/3', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(1, 3))).to.eq('0.33333') + }) + it('is correct for 100/3', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(100, 3))).to.eq('33.333') + }) + it('is correct for 1_000_000/3', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(1_000_000, 3))).to.eq('333330') + }) + it('1e-18 still prints 5 sig figs', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(1, 1e18), 18, 18)).to.eq('0.0000000000000000010000') + }) + it('accounts for decimal differences', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(1e6, 1e18), 18, 6)).to.eq('1.0000') + }) + it('accounts for decimal differences in reverse', () => { + expect(formatSqrtRatioX96(encodePriceSqrt(1e18, 1e6), 6, 18)).to.eq('1.0000') + }) +}) diff --git a/lib/uniswap/v3-periphery/test/shared/formatSqrtRatioX96.ts b/lib/uniswap/v3-periphery/test/shared/formatSqrtRatioX96.ts new file mode 100644 index 0000000..7f93d55 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/formatSqrtRatioX96.ts @@ -0,0 +1,30 @@ +import { BigNumber } from 'ethers' +import Decimal from 'decimal.js' + +const TWO = BigNumber.from(2) +const TEN = BigNumber.from(10) +const FIVE_SIG_FIGS_POW = new Decimal(10).pow(5) + +export function formatSqrtRatioX96( + sqrtRatioX96: BigNumber | number, + decimalsToken0: number = 18, + decimalsToken1: number = 18 +): string { + Decimal.set({ toExpPos: 9_999_999, toExpNeg: -9_999_999 }) + + let ratioNum = ((parseInt(sqrtRatioX96.toString()) / 2 ** 96) ** 2).toPrecision(5) + let ratio = new Decimal(ratioNum.toString()) + + // adjust for decimals + if (decimalsToken1 < decimalsToken0) { + ratio = ratio.mul(TEN.pow(decimalsToken0 - decimalsToken1).toString()) + } else if (decimalsToken0 < decimalsToken1) { + ratio = ratio.div(TEN.pow(decimalsToken1 - decimalsToken0).toString()) + } + + if (ratio.lessThan(FIVE_SIG_FIGS_POW)) { + return ratio.toPrecision(5) + } + + return ratio.toString() +} diff --git a/lib/uniswap/v3-periphery/test/shared/getPermitNFTSignature.ts b/lib/uniswap/v3-periphery/test/shared/getPermitNFTSignature.ts new file mode 100644 index 0000000..697af96 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/getPermitNFTSignature.ts @@ -0,0 +1,57 @@ +import { BigNumber, BigNumberish, constants, Signature, Wallet } from 'ethers' +import { splitSignature } from 'ethers/lib/utils' +import { NonfungiblePositionManager } from '../../typechain' + +export default async function getPermitNFTSignature( + wallet: Wallet, + positionManager: NonfungiblePositionManager, + spender: string, + tokenId: BigNumberish, + deadline: BigNumberish = constants.MaxUint256, + permitConfig?: { nonce?: BigNumberish; name?: string; chainId?: number; version?: string } +): Promise { + const [nonce, name, version, chainId] = await Promise.all([ + permitConfig?.nonce ?? positionManager.positions(tokenId).then((p) => p.nonce), + permitConfig?.name ?? positionManager.name(), + permitConfig?.version ?? '1', + permitConfig?.chainId ?? wallet.getChainId(), + ]) + + return splitSignature( + await wallet._signTypedData( + { + name, + version, + chainId, + verifyingContract: positionManager.address, + }, + { + Permit: [ + { + name: 'spender', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + { + name: 'nonce', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + ], + }, + { + owner: wallet.address, + spender, + tokenId, + nonce, + deadline, + } + ) + ) +} diff --git a/lib/uniswap/v3-periphery/test/shared/path.ts b/lib/uniswap/v3-periphery/test/shared/path.ts new file mode 100644 index 0000000..99418b1 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/path.ts @@ -0,0 +1,61 @@ +import { utils } from 'ethers' +import { FeeAmount } from './constants' + +const ADDR_SIZE = 20 +const FEE_SIZE = 3 +const OFFSET = ADDR_SIZE + FEE_SIZE +const DATA_SIZE = OFFSET + ADDR_SIZE + +export function encodePath(path: string[], fees: FeeAmount[]): string { + if (path.length != fees.length + 1) { + throw new Error('path/fee lengths do not match') + } + + let encoded = '0x' + for (let i = 0; i < fees.length; i++) { + // 20 byte encoding of the address + encoded += path[i].slice(2) + // 3 byte encoding of the fee + encoded += fees[i].toString(16).padStart(2 * FEE_SIZE, '0') + } + // encode the final token + encoded += path[path.length - 1].slice(2) + + return encoded.toLowerCase() +} + +function decodeOne(tokenFeeToken: Buffer): [[string, string], number] { + // reads the first 20 bytes for the token address + const tokenABuf = tokenFeeToken.slice(0, ADDR_SIZE) + const tokenA = utils.getAddress('0x' + tokenABuf.toString('hex')) + + // reads the next 2 bytes for the fee + const feeBuf = tokenFeeToken.slice(ADDR_SIZE, OFFSET) + const fee = feeBuf.readUIntBE(0, FEE_SIZE) + + // reads the next 20 bytes for the token address + const tokenBBuf = tokenFeeToken.slice(OFFSET, DATA_SIZE) + const tokenB = utils.getAddress('0x' + tokenBBuf.toString('hex')) + + return [[tokenA, tokenB], fee] +} + +export function decodePath(path: string): [string[], number[]] { + let data = Buffer.from(path.slice(2), 'hex') + + let tokens: string[] = [] + let fees: number[] = [] + let i = 0 + let finalToken: string = '' + while (data.length >= DATA_SIZE) { + const [[tokenA, tokenB], fee] = decodeOne(data) + finalToken = tokenB + tokens = [...tokens, tokenA] + fees = [...fees, fee] + data = data.slice((i + 1) * OFFSET) + i += 1 + } + tokens = [...tokens, finalToken] + + return [tokens, fees] +} diff --git a/lib/uniswap/v3-periphery/test/shared/permit.ts b/lib/uniswap/v3-periphery/test/shared/permit.ts new file mode 100644 index 0000000..f3ec85d --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/permit.ts @@ -0,0 +1,61 @@ +import { BigNumberish, constants, Signature, Wallet } from 'ethers' +import { splitSignature } from 'ethers/lib/utils' +import { TestERC20, TestERC20PermitAllowed } from '../../typechain' + +export async function getPermitSignature( + wallet: Wallet, + token: TestERC20 | TestERC20PermitAllowed, + spender: string, + value: BigNumberish = constants.MaxUint256, + deadline = constants.MaxUint256, + permitConfig?: { nonce?: BigNumberish; name?: string; chainId?: number; version?: string } +): Promise { + const [nonce, name, version, chainId] = await Promise.all([ + permitConfig?.nonce ?? token.nonces(wallet.address), + permitConfig?.name ?? token.name(), + permitConfig?.version ?? '1', + permitConfig?.chainId ?? wallet.getChainId(), + ]) + + return splitSignature( + await wallet._signTypedData( + { + name, + version, + chainId, + verifyingContract: token.address, + }, + { + Permit: [ + { + name: 'owner', + type: 'address', + }, + { + name: 'spender', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + { + name: 'nonce', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + ], + }, + { + owner: wallet.address, + spender, + value, + nonce, + deadline, + } + ) + ) +} diff --git a/lib/uniswap/v3-periphery/test/shared/poolAtAddress.ts b/lib/uniswap/v3-periphery/test/shared/poolAtAddress.ts new file mode 100644 index 0000000..b877813 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/poolAtAddress.ts @@ -0,0 +1,7 @@ +import { abi as POOL_ABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json' +import { Contract, Wallet } from 'ethers' +import { IUniswapV3Pool } from '../../typechain' + +export default function poolAtAddress(address: string, wallet: Wallet): IUniswapV3Pool { + return new Contract(address, POOL_ABI, wallet) as IUniswapV3Pool +} diff --git a/lib/uniswap/v3-periphery/test/shared/quoter.ts b/lib/uniswap/v3-periphery/test/shared/quoter.ts new file mode 100644 index 0000000..48d1b52 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/quoter.ts @@ -0,0 +1,153 @@ +import { Wallet } from 'ethers' +import { MockTimeNonfungiblePositionManager } from '../../typechain' +import { FeeAmount, TICK_SPACINGS } from './constants' +import { encodePriceSqrt } from './encodePriceSqrt' +import { getMaxTick, getMinTick } from './ticks' + +export async function createPool( + nft: MockTimeNonfungiblePositionManager, + wallet: Wallet, + tokenAddressA: string, + tokenAddressB: string +) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, FeeAmount.MEDIUM, encodePriceSqrt(1, 1)) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallet.address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams) +} + +export async function createPoolWithMultiplePositions( + nft: MockTimeNonfungiblePositionManager, + wallet: Wallet, + tokenAddressA: string, + tokenAddressB: string +) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, FeeAmount.MEDIUM, encodePriceSqrt(1, 1)) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallet.address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + await nft.mint(liquidityParams) + + const liquidityParams2 = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: -60, + tickUpper: 60, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + await nft.mint(liquidityParams2) + + const liquidityParams3 = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: -120, + tickUpper: 120, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams3) +} + +export async function createPoolWithZeroTickInitialized( + nft: MockTimeNonfungiblePositionManager, + wallet: Wallet, + tokenAddressA: string, + tokenAddressB: string +) { + if (tokenAddressA.toLowerCase() > tokenAddressB.toLowerCase()) + [tokenAddressA, tokenAddressB] = [tokenAddressB, tokenAddressA] + + await nft.createAndInitializePoolIfNecessary(tokenAddressA, tokenAddressB, FeeAmount.MEDIUM, encodePriceSqrt(1, 1)) + + const liquidityParams = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + recipient: wallet.address, + amount0Desired: 1000000, + amount1Desired: 1000000, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + await nft.mint(liquidityParams) + + const liquidityParams2 = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: 0, + tickUpper: 60, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + await nft.mint(liquidityParams2) + + const liquidityParams3 = { + token0: tokenAddressA, + token1: tokenAddressB, + fee: FeeAmount.MEDIUM, + tickLower: -120, + tickUpper: 0, + recipient: wallet.address, + amount0Desired: 100, + amount1Desired: 100, + amount0Min: 0, + amount1Min: 0, + deadline: 1, + } + + return nft.mint(liquidityParams3) +} diff --git a/lib/uniswap/v3-periphery/test/shared/snapshotGasCost.ts b/lib/uniswap/v3-periphery/test/shared/snapshotGasCost.ts new file mode 100644 index 0000000..5a606d2 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/snapshotGasCost.ts @@ -0,0 +1,27 @@ +import { TransactionReceipt, TransactionResponse } from '@ethersproject/abstract-provider' +import { expect } from './expect' +import { Contract, BigNumber, ContractTransaction } from 'ethers' + +export default async function snapshotGasCost( + x: + | TransactionResponse + | Promise + | ContractTransaction + | Promise + | TransactionReceipt + | Promise + | BigNumber + | Contract + | Promise +): Promise { + const resolved = await x + if ('deployTransaction' in resolved) { + const receipt = await resolved.deployTransaction.wait() + expect(receipt.gasUsed.toNumber()).toMatchSnapshot() + } else if ('wait' in resolved) { + const waited = await resolved.wait() + expect(waited.gasUsed.toNumber()).toMatchSnapshot() + } else if (BigNumber.isBigNumber(resolved)) { + expect(resolved.toNumber()).toMatchSnapshot() + } +} diff --git a/lib/uniswap/v3-periphery/test/shared/ticks.ts b/lib/uniswap/v3-periphery/test/shared/ticks.ts new file mode 100644 index 0000000..40aa294 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/ticks.ts @@ -0,0 +1,9 @@ +import { BigNumber } from 'ethers' + +export const getMinTick = (tickSpacing: number) => Math.ceil(-887272 / tickSpacing) * tickSpacing +export const getMaxTick = (tickSpacing: number) => Math.floor(887272 / tickSpacing) * tickSpacing +export const getMaxLiquidityPerTick = (tickSpacing: number) => + BigNumber.from(2) + .pow(128) + .sub(1) + .div((getMaxTick(tickSpacing) - getMinTick(tickSpacing)) / tickSpacing + 1) diff --git a/lib/uniswap/v3-periphery/test/shared/tokenSort.ts b/lib/uniswap/v3-periphery/test/shared/tokenSort.ts new file mode 100644 index 0000000..bdab508 --- /dev/null +++ b/lib/uniswap/v3-periphery/test/shared/tokenSort.ts @@ -0,0 +1,10 @@ +export function compareToken(a: { address: string }, b: { address: string }): -1 | 1 { + return a.address.toLowerCase() < b.address.toLowerCase() ? -1 : 1 +} + +export function sortedTokens( + a: { address: string }, + b: { address: string } +): [typeof a, typeof b] | [typeof b, typeof a] { + return compareToken(a, b) < 0 ? [a, b] : [b, a] +} diff --git a/lib/uniswap/v3-periphery/testnet-deploys.md b/lib/uniswap/v3-periphery/testnet-deploys.md new file mode 100644 index 0000000..dfaa185 --- /dev/null +++ b/lib/uniswap/v3-periphery/testnet-deploys.md @@ -0,0 +1,3 @@ +## Uniswap V3 is live on Mainnet + +See the addresses [here](./deploys.md). diff --git a/lib/uniswap/v3-periphery/tsconfig.json b/lib/uniswap/v3-periphery/tsconfig.json new file mode 100644 index 0000000..69ab585 --- /dev/null +++ b/lib/uniswap/v3-periphery/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "outDir": "dist", + "typeRoots": ["./typechain", "./node_modules/@types"], + "types": ["@nomiclabs/hardhat-ethers", "@nomiclabs/hardhat-waffle"] + }, + "include": ["./test"], + "files": ["./hardhat.config.ts"] +} diff --git a/lib/uniswap/v3-periphery/yarn.lock b/lib/uniswap/v3-periphery/yarn.lock new file mode 100644 index 0000000..fd16018 --- /dev/null +++ b/lib/uniswap/v3-periphery/yarn.lock @@ -0,0 +1,8110 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/helper-validator-identifier@^7.10.4", "@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@ensdomains/ens@^0.4.4": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.5.tgz#e0aebc005afdc066447c6e22feb4eda89a5edbfc" + dependencies: + bluebird "^3.5.2" + eth-ens-namehash "^2.0.8" + solc "^0.4.20" + testrpc "0.0.1" + web3-utils "^1.0.0-beta.31" + +"@ensdomains/resolver@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" + +"@ethereum-waffle/chai@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/chai/-/chai-3.2.1.tgz#5cb542b2a323adf0bc2dda00f48b0eb85944d8ab" + dependencies: + "@ethereum-waffle/provider" "^3.2.1" + ethers "^5.0.0" + +"@ethereum-waffle/compiler@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/compiler/-/compiler-3.2.1.tgz#612a9056285a94ce28eb57b895770ad10e438bf9" + dependencies: + "@resolver-engine/imports" "^0.3.3" + "@resolver-engine/imports-fs" "^0.3.3" + "@types/mkdirp" "^0.5.2" + "@types/node-fetch" "^2.5.5" + ethers "^5.0.1" + mkdirp "^0.5.1" + node-fetch "^2.6.0" + solc "^0.6.3" + +"@ethereum-waffle/ens@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/ens/-/ens-3.2.1.tgz#9f369112d62f7aa88d010be4d133b6d0f5e8c492" + dependencies: + "@ensdomains/ens" "^0.4.4" + "@ensdomains/resolver" "^0.2.4" + ethers "^5.0.1" + +"@ethereum-waffle/mock-contract@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/mock-contract/-/mock-contract-3.2.1.tgz#bf5f63f61c9749eb3270108893a88ff161e68f58" + dependencies: + "@ethersproject/abi" "^5.0.1" + ethers "^5.0.1" + +"@ethereum-waffle/provider@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@ethereum-waffle/provider/-/provider-3.2.1.tgz#d84c0603936f09afa69ecb671d56f527e9818e71" + dependencies: + "@ethereum-waffle/ens" "^3.2.1" + ethers "^5.0.1" + ganache-core "^2.10.2" + patch-package "^6.2.2" + postinstall-postinstall "^2.1.0" + +"@ethereumjs/block@^3.4.0", "@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.0.tgz#5cf89ea748607597a3f8b038abc986e4ac0b05db" + integrity sha512-dqLo1LtsLG+Oelu5S5tWUDG0pah3QUwV5TJZy2cm19BXDr4ka/S9XBSgao0i09gTcuPlovlHgcs6d7EZ37urjQ== + dependencies: + "@ethereumjs/common" "^2.6.0" + "@ethereumjs/tx" "^3.4.0" + ethereumjs-util "^7.1.3" + merkle-patricia-tree "^4.2.2" + +"@ethereumjs/blockchain@^5.4.0", "@ethereumjs/blockchain@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.1.tgz#60f1f50592c06cc47e1704800b88b7d32f609742" + integrity sha512-JS2jeKxl3tlaa5oXrZ8mGoVBCz6YqsGG350XVNtHAtNZXKk7pU3rH4xzF2ru42fksMMqzFLzKh9l4EQzmNWDqA== + dependencies: + "@ethereumjs/block" "^3.6.0" + "@ethereumjs/common" "^2.6.0" + "@ethereumjs/ethash" "^1.1.0" + debug "^2.2.0" + ethereumjs-util "^7.1.3" + level-mem "^5.0.1" + lru-cache "^5.1.1" + semaphore-async-await "^1.5.1" + +"@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.0.tgz#feb96fb154da41ee2cc2c5df667621a440f36348" + integrity sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.3" + +"@ethereumjs/ethash@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" + integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== + dependencies: + "@ethereumjs/block" "^3.5.0" + "@types/levelup" "^4.3.0" + buffer-xor "^2.0.1" + ethereumjs-util "^7.1.1" + miller-rabin "^4.0.0" + +"@ethereumjs/tx@^3.3.0", "@ethereumjs/tx@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.4.0.tgz#7eb1947eefa55eb9cf05b3ca116fb7a3dbd0bce7" + integrity sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw== + dependencies: + "@ethereumjs/common" "^2.6.0" + ethereumjs-util "^7.1.3" + +"@ethereumjs/vm@^5.5.2": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.6.0.tgz#e0ca62af07de820143674c30b776b86c1983a464" + integrity sha512-J2m/OgjjiGdWF2P9bj/4LnZQ1zRoZhY8mRNVw/N3tXliGI8ai1sI1mlDPkLpeUUM4vq54gH6n0ZlSpz8U/qlYQ== + dependencies: + "@ethereumjs/block" "^3.6.0" + "@ethereumjs/blockchain" "^5.5.0" + "@ethereumjs/common" "^2.6.0" + "@ethereumjs/tx" "^3.4.0" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^2.2.0" + ethereumjs-util "^7.1.3" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.2.2" + rustbn.js "~0.2.0" + +"@ethersproject/abi@5.0.0-beta.153": + version "5.0.0-beta.153" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" + dependencies: + "@ethersproject/address" ">=5.0.0-beta.128" + "@ethersproject/bignumber" ">=5.0.0-beta.130" + "@ethersproject/bytes" ">=5.0.0-beta.129" + "@ethersproject/constants" ">=5.0.0-beta.128" + "@ethersproject/hash" ">=5.0.0-beta.128" + "@ethersproject/keccak256" ">=5.0.0-beta.127" + "@ethersproject/logger" ">=5.0.0-beta.129" + "@ethersproject/properties" ">=5.0.0-beta.131" + "@ethersproject/strings" ">=5.0.0-beta.130" + +"@ethersproject/abi@5.0.9", "@ethersproject/abi@^5.0.1", "@ethersproject/abi@^5.0.5": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.9.tgz#738c1c557e56d8f395a5a27caef9b0449bc85a10" + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abi@^5.1.2": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" + integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/abstract-provider@5.0.7", "@ethersproject/abstract-provider@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.7.tgz#04ee3bfe43323384e7fecf6c774975b8dec4bdc9" + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + +"@ethersproject/abstract-provider@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" + integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + +"@ethersproject/abstract-signer@5.0.9", "@ethersproject/abstract-signer@^5.0.4", "@ethersproject/abstract-signer@^5.0.6": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.9.tgz#238ddc06031aeb9dfceee2add965292d7dd1acbf" + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/abstract-signer@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" + integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/address@5.0.8", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.8.tgz#0c551659144a5a7643c6bea337149d410825298f" + dependencies: + "@ethersproject/bignumber" "^5.0.10" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/rlp" "^5.0.3" + +"@ethersproject/address@^5.0.2": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.1.0.tgz#3854fd7ebcb6af7597de66f847c3345dae735b58" + integrity sha512-rfWQR12eHn2cpstCFS4RF7oGjfbkZb0oqep+BfrT+gWEGWG2IowJvIsacPOvzyS1jhNF4MQ4BS59B04Mbovteg== + dependencies: + "@ethersproject/bignumber" "^5.1.0" + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/keccak256" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + "@ethersproject/rlp" "^5.1.0" + +"@ethersproject/address@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" + integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + +"@ethersproject/base64@5.0.6", "@ethersproject/base64@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.6.tgz#26311ebf29ea3d0b9c300ccf3e1fdc44b7481516" + dependencies: + "@ethersproject/bytes" "^5.0.4" + +"@ethersproject/base64@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" + integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + +"@ethersproject/basex@5.0.6", "@ethersproject/basex@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.6.tgz#ab95c32e48288a3d868726463506641cb1e9fb6b" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/bignumber@5.0.12", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.10", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.0.8": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.12.tgz#fe4a78667d7cb01790f75131147e82d6ea7e7cba" + dependencies: + "@ethersproject/bytes" "^5.0.8" + "@ethersproject/logger" "^5.0.5" + bn.js "^4.4.0" + +"@ethersproject/bignumber@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.1.1.tgz#84812695253ccbc639117f7ac49ee1529b68e637" + integrity sha512-AVz5iqz7+70RIqoQTznsdJ6DOVBYciNlvO+AlQmPTB6ofCvoihI9bQdr6wljsX+d5W7Yc4nyvQvP4JMzg0Agig== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + bn.js "^4.4.0" + +"@ethersproject/bignumber@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" + integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + bn.js "^4.11.9" + +"@ethersproject/bytes@5.0.8", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.8.tgz#cf1246a6a386086e590063a4602b1ffb6cc43db1" + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/bytes@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.1.0.tgz#55dfa9c4c21df1b1b538be3accb50fb76d5facfd" + integrity sha512-sGTxb+LVjFxJcJeUswAIK6ncgOrh3D8c192iEJd7mLr95V6du119rRfYT/b87WPkZ5I3gRBUYIYXtdgCWACe8g== + dependencies: + "@ethersproject/logger" "^5.1.0" + +"@ethersproject/bytes@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/constants@5.0.7", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.7.tgz#44ff979e5781b17c8c6901266896c3ee745f4e7e" + dependencies: + "@ethersproject/bignumber" "^5.0.7" + +"@ethersproject/constants@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" + integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + +"@ethersproject/contracts@5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.8.tgz#71d3ba16853a1555be2e161a6741df186f81c73b" + dependencies: + "@ethersproject/abi" "^5.0.5" + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + +"@ethersproject/hash@5.0.9", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.9.tgz#81252a848185b584aa600db4a1a68cad9229a4d4" + dependencies: + "@ethersproject/abstract-signer" "^5.0.6" + "@ethersproject/address" "^5.0.5" + "@ethersproject/bignumber" "^5.0.8" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.4" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/hash@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" + integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/hdnode@5.0.7", "@ethersproject/hdnode@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.7.tgz#c7bce94a337ea65e37c46bab09a83e1c1a555d99" + dependencies: + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/basex" "^5.0.3" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/pbkdf2" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/wordlists" "^5.0.4" + +"@ethersproject/json-wallets@5.0.9", "@ethersproject/json-wallets@^5.0.6": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.9.tgz#2e1708c2854c4ab764e35920bd1f44c948b95434" + dependencies: + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/hdnode" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/pbkdf2" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.0.6", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.6.tgz#5b5ba715ef1be86efde5c271f896fa0daf0e1efe" + dependencies: + "@ethersproject/bytes" "^5.0.4" + js-sha3 "0.5.7" + +"@ethersproject/keccak256@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.1.0.tgz#fdcd88fb13bfef4271b225cdd8dec4d315c8e60e" + integrity sha512-vrTB1W6AEYoadww5c9UyVJ2YcSiyIUTNDRccZIgwTmFFoSHwBtcvG1hqy9RzJ1T0bMdATbM9Hfx2mJ6H0i7Hig== + dependencies: + "@ethersproject/bytes" "^5.1.0" + js-sha3 "0.5.7" + +"@ethersproject/keccak256@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" + integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.0.8", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.8.tgz#135c1903d35c878265f3cbf2b287042c4c20d5d4" + +"@ethersproject/logger@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.1.0.tgz#4cdeeefac029373349d5818f39c31b82cc6d9bbf" + integrity sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw== + +"@ethersproject/logger@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + +"@ethersproject/networks@5.0.6", "@ethersproject/networks@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.6.tgz#4d6586bbebfde1c027504ebf6dfb783b29c3803a" + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/networks@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" + integrity sha512-KWfP3xOnJeF89Uf/FCJdV1a2aDJe5XTN2N52p4fcQ34QhDqQFkgQKZ39VGtiqUgHcLI8DfT0l9azC3KFTunqtA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/pbkdf2@5.0.6", "@ethersproject/pbkdf2@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.6.tgz#105dbfb08cd5fcf33869b42bfdc35a3ebd978cbd" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/sha2" "^5.0.3" + +"@ethersproject/properties@5.0.6", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.4": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.6.tgz#44d82aaa294816fd63333e7def42426cf0e87b3b" + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/properties@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" + integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/providers@5.0.17": + version "5.0.17" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.17.tgz#f380e7831149e24e7a1c6c9b5fb1d6dfc729d024" + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/basex" "^5.0.3" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/networks" "^5.0.3" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/web" "^5.0.6" + bech32 "1.1.4" + ws "7.2.3" + +"@ethersproject/random@5.0.6", "@ethersproject/random@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.6.tgz#9be80a1065f2b8e6f321dccb3ebeb4886cac9ea4" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/rlp@5.0.6", "@ethersproject/rlp@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.6.tgz#29f9097348a3c330811997433b7df89ab51cd644" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/rlp@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.1.0.tgz#700f4f071c27fa298d3c1d637485fefe919dd084" + integrity sha512-vDTyHIwNPrecy55gKGZ47eJZhBm8LLBxihzi5ou+zrSvYTpkSTWRcKUlXFDFQVwfWB+P5PGyERAdiDEI76clxw== + dependencies: + "@ethersproject/bytes" "^5.1.0" + "@ethersproject/logger" "^5.1.0" + +"@ethersproject/rlp@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" + integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/sha2@5.0.6", "@ethersproject/sha2@^5.0.3": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.6.tgz#175116dc10b866a0a381f6316d094bcc510bee3c" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + hash.js "1.1.3" + +"@ethersproject/signing-key@5.0.7", "@ethersproject/signing-key@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.7.tgz#d03bfc5f565efb962bafebf8e6965e70d1c46d31" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + elliptic "6.5.3" + +"@ethersproject/signing-key@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" + integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.7.tgz#72a3455f47a454db2dcf363992d42e9045dc7fce" + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/sha2" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/strings@5.0.7", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.7.tgz#8dc68f794c9e2901f3b75e53b2afbcb6b6c15037" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/strings@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" + integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/transactions@5.0.8", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.5": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.8.tgz#3b4d7041e13b957a9c4f131e0aea9dae7b6f5a23" + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + +"@ethersproject/transactions@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" + integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + +"@ethersproject/units@5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.8.tgz#563325b20fe1eceff7b61857711d5e2b3f38fd09" + dependencies: + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/wallet@5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.9.tgz#976c7d950489c40308d676869d24e59ab7b82ad1" + dependencies: + "@ethersproject/abstract-provider" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.4" + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/hdnode" "^5.0.4" + "@ethersproject/json-wallets" "^5.0.6" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/random" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + "@ethersproject/transactions" "^5.0.5" + "@ethersproject/wordlists" "^5.0.4" + +"@ethersproject/web@5.0.11", "@ethersproject/web@^5.0.6": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.11.tgz#d47da612b958b4439e415782a53c8f8461522d68" + dependencies: + "@ethersproject/base64" "^5.0.3" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/web@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.0.tgz#0e5bb21a2b58fb4960a705bfc6522a6acf461e28" + integrity sha512-BEgY0eL5oH4mAo37TNYVrFeHsIXLRxggCRG/ksRIxI2X5uj5IsjGmcNiRN/VirQOlBxcUhCgHhaDLG4m6XAVoA== + dependencies: + "@ethersproject/base64" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/wordlists@5.0.7", "@ethersproject/wordlists@^5.0.4": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.7.tgz#4e5ad38cfbef746b196a3290c0d41696eb7ab468" + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/test-result@^26.5.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@nomiclabs/hardhat-ethers@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" + integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== + +"@nomiclabs/hardhat-etherscan@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.8.tgz#e206275e96962cd15e5ba9148b44388bc922d8c2" + integrity sha512-0+rj0SsZotVOcTLyDOxnOc3Gulo8upo0rsw/h+gBPcmtj91YqYJNhdARHoBxOhhE8z+5IUQPx+Dii04lXT14PA== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^5.0.2" + debug "^4.1.1" + fs-extra "^7.0.1" + node-fetch "^2.6.0" + semver "^6.3.0" + +"@nomiclabs/hardhat-waffle@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.1.tgz#5d43654fba780720c5033dea240fe14f70ef4bd2" + integrity sha512-2YR2V5zTiztSH9n8BYWgtv3Q+EL0N5Ltm1PAr5z20uAY4SkkfylJ98CIqt18XFvxTD5x4K2wKBzddjV9ViDAZQ== + dependencies: + "@types/sinon-chai" "^3.2.3" + "@types/web3" "1.0.19" + +"@openzeppelin/contracts@3.4.2-solc-0.7": + version "3.4.2-solc-0.7" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2-solc-0.7.tgz#38f4dbab672631034076ccdf2f3201fab1726635" + integrity sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA== + +"@resolver-engine/core@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967" + dependencies: + debug "^3.1.0" + is-url "^1.2.4" + request "^2.85.0" + +"@resolver-engine/fs@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/fs/-/fs-0.3.3.tgz#fbf83fa0c4f60154a82c817d2fe3f3b0c049a973" + dependencies: + "@resolver-engine/core" "^0.3.3" + debug "^3.1.0" + +"@resolver-engine/imports-fs@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/imports-fs/-/imports-fs-0.3.3.tgz#4085db4b8d3c03feb7a425fbfcf5325c0d1e6c1b" + dependencies: + "@resolver-engine/fs" "^0.3.3" + "@resolver-engine/imports" "^0.3.3" + debug "^3.1.0" + +"@resolver-engine/imports@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@resolver-engine/imports/-/imports-0.3.3.tgz#badfb513bb3ff3c1ee9fd56073e3144245588bcc" + dependencies: + "@resolver-engine/core" "^0.3.3" + debug "^3.1.0" + hosted-git-info "^2.6.0" + path-browserify "^1.0.0" + url "^0.11.0" + +"@sentry/core@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.29.2.tgz#9e05fe197234161d57aabaf52fab336a7c520d81" + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/minimal" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/hub@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.29.2.tgz#208f10fe6674695575ad74182a1151f71d6df00a" + dependencies: + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/minimal@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.29.2.tgz#420bebac8d03d30980fdb05c72d7b253d8aa541b" + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/types" "5.29.2" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.29.2.tgz#f0f0b4b2be63c9ddd702729fab998cead271dff1" + dependencies: + "@sentry/core" "5.29.2" + "@sentry/hub" "5.29.2" + "@sentry/tracing" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.29.2.tgz#6012788547d2ab7893799d82c4941bda145dcd47" + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/minimal" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/types@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.29.2.tgz#ac87383df1222c2d9b9f8f9ed7a6b86ea41a098a" + +"@sentry/utils@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.29.2.tgz#99a5cdda2ea19d34a41932f138d470adcb3ee673" + dependencies: + "@sentry/types" "5.29.2" + tslib "^1.9.3" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + +"@solidity-parser/parser@^0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.1.tgz#10ce249890d32ba500e9ce449e60a2b26b11be7a" + integrity sha512-ikxVpwskNxEp2fvYS1BdRImnevHmM97zdPFBa1cVtjtNpoqCm/EmljATTZk0s9G/zsN5ZbPf9OAIAW4gbBJiRA== + +"@solidity-parser/parser@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.0.tgz#d51f074efb0acce0e953ec48133561ed710cebc0" + integrity sha512-cX0JJRcmPtNUJpzD2K7FdA7qQsTOk1UZnFx2k7qAg9ZRvuaH5NBe5IEdBMXGlmf2+FmjhqbygJ26H8l2SV7aKQ== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@solidity-parser/parser@^0.8.2": + version "0.8.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.8.2.tgz#a6a5e93ac8dca6884a99a532f133beba59b87b69" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + dependencies: + defer-to-connect "^1.0.1" + +"@typechain/ethers-v5@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-4.0.0.tgz#2a8be5e108d23f3b8e6354d1618fdc2abcb00b07" + +"@types/abstract-leveldown@*": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.1.tgz#3c7750d0186b954c7f2d2f6acc8c3c7ba0c3412e" + integrity sha512-wYxU3kp5zItbxKmeRYCEplS2MW7DzyBnxPGj+GJVHZEUZiK/nn5Ei1sUFgURDh+X051+zsGe28iud3oHjrYWQQ== + +"@types/babel__traverse@^7.0.4": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.11.0.tgz#b9a1efa635201ba9bc850323a8793ee2d36c04a0" + dependencies: + "@babel/types" "^7.3.0" + +"@types/bn.js@*", "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/chai@*", "@types/chai@^4.2.6": + version "4.2.14" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193" + +"@types/graceful-fs@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.4.tgz#4ff9f641a7c6d1a3508ff88bc3141b152772e753" + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/levelup@^4.3.0": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.1.tgz#7a53b9fd510716e11b2065332790fdf5f9b950b9" + integrity sha512-n//PeTpbHLjMLTIgW5B/g06W/6iuTBHuvUka2nFL9APMSVMNe2r4enADfu3CIE9IyV9E+uquf9OEQQqrDeg24A== + dependencies: + "@types/abstract-leveldown" "*" + "@types/node" "*" + +"@types/lru-cache@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" + +"@types/mkdirp@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" + dependencies: + "@types/node" "*" + +"@types/mocha@^5.2.7": + version "5.2.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" + +"@types/node-fetch@^2.5.5": + version "2.5.7" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "14.14.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae" + +"@types/node@^12.12.6": + version "12.19.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.9.tgz#990ad687ad8b26ef6dcc34a4f69c33d40c95b679" + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + dependencies: + "@types/node" "*" + +"@types/prettier@^2.0.0", "@types/prettier@^2.1.1": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00" + +"@types/resolve@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" + dependencies: + "@types/node" "*" + +"@types/sinon-chai@^3.2.3": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48" + dependencies: + "@types/chai" "*" + "@types/sinon" "*" + +"@types/sinon@*": + version "9.0.10" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.10.tgz#7fb9bcb6794262482859cab66d59132fca18fcf7" + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" + +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + +"@types/underscore@*": + version "1.10.24" + resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.10.24.tgz#dede004deed3b3f99c4db0bdb9ee21cae25befdd" + +"@types/web3@1.0.19": + version "1.0.19" + resolved "https://registry.yarnpkg.com/@types/web3/-/web3-1.0.19.tgz#46b85d91d398ded9ab7c85a5dd57cb33ac558924" + dependencies: + "@types/bn.js" "*" + "@types/underscore" "*" + +"@types/yargs-parser@*": + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + +"@types/yargs@^15.0.0": + version "15.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.12.tgz#6234ce3e3e3fa32c5db301a170f96a599c960d74" + dependencies: + "@types/yargs-parser" "*" + +"@uniswap/lib@^4.0.1-alpha": + version "4.0.1-alpha" + resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-4.0.1-alpha.tgz#2881008e55f075344675b3bca93f020b028fbd02" + +"@uniswap/v2-core@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425" + +"@uniswap/v3-core@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0.tgz#6c24adacc4c25dceee0ba3ca142b35adbd7e359d" + integrity sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA== + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + dependencies: + event-target-shim "^5.0.0" + +abstract-leveldown@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^2.4.1, abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz#f7128e1f86ccabf7d2893077ce5d06d798e386c6" + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-jsx@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + dependencies: + debug "4" + +ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + +ansi-escapes@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + dependencies: + type-fest "^0.11.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3, anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-back@^1.0.3, array-back@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b" + dependencies: + typical "^2.6.0" + +array-back@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-2.0.0.tgz#6877471d51ecc9c9bfa6136fb6c7d5fe69748022" + dependencies: + typical "^2.6.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + +async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + +async@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + dependencies: + lodash "^4.17.11" + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.0.14, babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babelify@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" + dependencies: + babel-core "^6.0.14" + object-assign "^4.0.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + +base64-sol@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/base64-sol/-/base64-sol-1.0.1.tgz#91317aa341f0bc763811783c5729f1c2574600f6" + integrity sha512-ld3cCNMeXt4uJXmLZBHFGMvVpK9KsLVEhPpFRXnvSVAqABKbuNZg/+dsq3NuM+wxFLb/UrVkz7m1ciWmkMfTbg== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + dependencies: + tweetnacl "^0.14.3" + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + +bip39@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235" + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + safe-buffer "^5.0.1" + unorm "^1.3.3" + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + +bluebird@^3.5.0, bluebird@^3.5.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^4.8.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer-xor@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" + dependencies: + safe-buffer "^5.1.1" + +buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.2.tgz#79f68631910f6b993d870fc77dc0a2894eb96cd5" + dependencies: + node-gyp-build "^4.2.0" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + +bytewise-core@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bytewise-core/-/bytewise-core-1.2.3.tgz#3fb410c7e91558eb1ab22a82834577aa6bd61d42" + dependencies: + typewise-core "^1.2" + +bytewise@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/bytewise/-/bytewise-1.1.0.tgz#1d13cbff717ae7158094aa881b35d081b387253e" + dependencies: + bytewise-core "^1.2.2" + typewise "^1.0.3" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +cachedown@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cachedown/-/cachedown-1.0.0.tgz#d43f036e4510696b31246d7db31ebf0f7ac32d15" + dependencies: + abstract-leveldown "^2.4.1" + lru-cache "^3.2.0" + +call-bind@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.0" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + +caniuse-lite@^1.0.30000844: + version "1.0.30001170" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7" + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + dependencies: + rsvp "^4.8.4" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +cbor@^5.0.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + dependencies: + functional-red-black-tree "^1.0.1" + +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +chokidar@^3.4.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^3.4.3: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + dependencies: + mimic-response "^1.0.0" + +clone@2.1.2, clone@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + +command-line-args@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46" + dependencies: + array-back "^2.0.0" + find-replace "^1.0.3" + typical "^2.6.1" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +convert-source-map@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-js-pure@^3.0.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.1.tgz#23f84048f366fdfcf52d3fd1c68fec349177d119" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +crc-32@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + dependencies: + ms "^2.1.1" + +debug@4, debug@^4.0.1, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + dependencies: + ms "2.1.2" + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + dependencies: + ms "^2.1.1" + +decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decimal.js@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + +deep-equal@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + dependencies: + abstract-leveldown "~2.6.0" + +deferred-leveldown@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz#0b0570087827bf480a23494b398f04c128c19a20" + dependencies: + abstract-leveldown "~5.0.0" + inherits "^2.0.3" + +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-to-object@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-to-object/-/dir-to-object-2.0.0.tgz#29723e9bd1c3e58e4f307bd04ff634c0370c8f8a" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + dependencies: + esutils "^2.0.2" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + +dotignore@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" + dependencies: + minimatch "^3.0.4" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +electron-to-chromium@^1.3.47: + version "1.3.633" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz#16dd5aec9de03894e8d14a1db4cda8a369b9b7fe" + +elliptic@6.5.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + +encoding-down@5.0.4, encoding-down@~5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614" + dependencies: + abstract-leveldown "^5.0.0" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + xtend "^4.0.1" + +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + dependencies: + once "^1.4.0" + +enquirer@^2.3.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + dependencies: + ansi-colors "^4.1.1" + +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: + version "1.17.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +esquery@^1.0.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +eth-block-tracker@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" + dependencies: + eth-query "^2.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.3" + ethjs-util "^0.1.3" + json-rpc-engine "^3.6.0" + pify "^2.3.0" + tape "^4.6.3" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-infura@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.1.tgz#26702a821067862b72d979c016fd611502c6057f" + dependencies: + cross-fetch "^2.1.1" + eth-json-rpc-middleware "^1.5.0" + json-rpc-engine "^3.4.0" + json-rpc-error "^2.0.0" + +eth-json-rpc-middleware@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" + dependencies: + async "^2.5.0" + eth-query "^2.1.2" + eth-tx-summary "^3.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.1.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^3.6.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + tape "^4.6.3" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-sig-util@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-1.4.2.tgz#8d958202c7edbaae839707fba6f09ff327606210" + dependencies: + ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" + ethereumjs-util "^5.1.1" + +eth-sig-util@^2.0.0, eth-sig-util@^2.5.2: + version "2.5.3" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.5.3.tgz#6938308b38226e0b3085435474900b03036abcbe" + dependencies: + buffer "^5.2.1" + elliptic "^6.4.0" + ethereumjs-abi "0.6.5" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.0" + tweetnacl-util "^0.15.0" + +eth-tx-summary@^3.1.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c" + dependencies: + async "^2.1.2" + clone "^2.0.0" + concat-stream "^1.5.1" + end-of-stream "^1.1.0" + eth-query "^2.0.2" + ethereumjs-block "^1.4.1" + ethereumjs-tx "^1.1.1" + ethereumjs-util "^5.0.1" + ethereumjs-vm "^2.6.0" + through2 "^2.0.3" + +ethashjs@~0.0.7: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ethashjs/-/ethashjs-0.0.8.tgz#227442f1bdee409a548fb04136e24c874f3aa6f9" + dependencies: + async "^2.1.2" + buffer-xor "^2.0.1" + ethereumjs-util "^7.0.2" + miller-rabin "^4.0.0" + +ethereum-bloom-filters@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz#b7b80735e385dbb7f944ce6b4533e24511306060" + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-waffle@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ethereum-waffle/-/ethereum-waffle-3.2.1.tgz#9d6d6b93484c5e1b77dfdeb646c050ed877e836e" + dependencies: + "@ethereum-waffle/chai" "^3.2.1" + "@ethereum-waffle/compiler" "^3.2.1" + "@ethereum-waffle/mock-contract" "^3.2.1" + "@ethereum-waffle/provider" "^3.2.1" + ethers "^5.0.1" + +ethereumjs-abi@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" + dependencies: + bn.js "^4.10.0" + ethereumjs-util "^4.3.0" + +ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": + version "0.6.8" + resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f" + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@3.0.0, ethereumjs-account@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz#728f060c8e0c6e87f1e987f751d3da25422570a9" + dependencies: + ethereumjs-util "^6.0.0" + rlp "^2.2.1" + safe-buffer "^5.1.1" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@2.2.2, ethereumjs-block@^2.2.2, ethereumjs-block@~2.2.0, ethereumjs-block@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-blockchain@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.4.tgz#30f2228dc35f6dcf94423692a6902604ae34960f" + dependencies: + async "^2.6.1" + ethashjs "~0.0.7" + ethereumjs-block "~2.2.2" + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.1.0" + flow-stoplight "^1.0.0" + level-mem "^3.0.1" + lru-cache "^5.1.1" + rlp "^2.2.2" + semaphore "^1.1.0" + +ethereumjs-common@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz#d3e82fc7c47c0cef95047f431a99485abc9bb1cd" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + +ethereumjs-tx@2.1.2, ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^4.3.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.1.tgz#f4bf9b3b515a484e3cc8781d61d9d980f7c83bd0" + dependencies: + bn.js "^4.8.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + rlp "^2.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^7.0.2: + version "7.0.7" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.7.tgz#484fb9c03b766b2ee64821281070616562fb5a59" + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz#b55d7b64dde3e3e45749e4c41288238edec32d23" + integrity sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethereumjs-vm@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz#e885e861424e373dbc556278f7259ff3fca5edab" + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + +ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz#685e9091645cee230ad125c007658833991ed474" + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^6.0.0" + randombytes "^2.0.6" + safe-buffer "^5.1.2" + scryptsy "^1.2.1" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@^5.0.0, ethers@^5.0.1, ethers@^5.0.8: + version "5.0.24" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.24.tgz#fbb8e4d35070d134f2eb846c07500b8c0eaef6d3" + dependencies: + "@ethersproject/abi" "5.0.9" + "@ethersproject/abstract-provider" "5.0.7" + "@ethersproject/abstract-signer" "5.0.9" + "@ethersproject/address" "5.0.8" + "@ethersproject/base64" "5.0.6" + "@ethersproject/basex" "5.0.6" + "@ethersproject/bignumber" "5.0.12" + "@ethersproject/bytes" "5.0.8" + "@ethersproject/constants" "5.0.7" + "@ethersproject/contracts" "5.0.8" + "@ethersproject/hash" "5.0.9" + "@ethersproject/hdnode" "5.0.7" + "@ethersproject/json-wallets" "5.0.9" + "@ethersproject/keccak256" "5.0.6" + "@ethersproject/logger" "5.0.8" + "@ethersproject/networks" "5.0.6" + "@ethersproject/pbkdf2" "5.0.6" + "@ethersproject/properties" "5.0.6" + "@ethersproject/providers" "5.0.17" + "@ethersproject/random" "5.0.6" + "@ethersproject/rlp" "5.0.6" + "@ethersproject/sha2" "5.0.6" + "@ethersproject/signing-key" "5.0.7" + "@ethersproject/solidity" "5.0.7" + "@ethersproject/strings" "5.0.7" + "@ethersproject/transactions" "5.0.8" + "@ethersproject/units" "5.0.8" + "@ethersproject/wallet" "5.0.9" + "@ethersproject/web" "5.0.11" + "@ethersproject/wordlists" "5.0.7" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + +events@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + dependencies: + checkpoint-store "^1.1.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fast-xml-parser@^3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" + integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + dependencies: + bser "2.1.1" + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + dependencies: + node-fetch "~1.7.1" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + dependencies: + flat-cache "^2.0.1" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-package-json@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083" + +find-replace@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" + dependencies: + array-back "^1.0.4" + test-value "^2.1.0" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-yarn-workspace-root@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" + dependencies: + fs-extra "^4.0.3" + micromatch "^3.1.4" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" + dependencies: + is-buffer "~2.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + +flow-stoplight@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" + +follow-redirects@^1.12.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" + +for-each@~0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^4.0.2, fs-extra@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.0, fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.2.1.tgz#1fb02ded2036a8ac288d507a65962bd87b97628d" + +fsevents@~2.1.1, fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1, function-bind@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +ganache-core@^2.10.2: + version "2.13.1" + resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.1.tgz#bf60399a2dd084e1090db91cbbc7ed3885dc01e4" + dependencies: + abstract-leveldown "3.0.0" + async "2.6.2" + bip39 "2.5.0" + cachedown "1.0.0" + clone "2.1.2" + debug "3.2.6" + encoding-down "5.0.4" + eth-sig-util "^2.0.0" + ethereumjs-abi "0.6.8" + ethereumjs-account "3.0.0" + ethereumjs-block "2.2.2" + ethereumjs-common "1.5.0" + ethereumjs-tx "2.1.2" + ethereumjs-util "6.2.1" + ethereumjs-vm "4.2.0" + heap "0.2.6" + keccak "3.0.1" + level-sublevel "6.6.4" + levelup "3.1.1" + lodash "4.17.20" + lru-cache "5.1.1" + merkle-patricia-tree "3.0.0" + patch-package "6.2.2" + seedrandom "3.0.1" + source-map-support "0.5.12" + tmp "0.1.0" + web3-provider-engine "14.2.1" + websocket "1.0.32" + optionalDependencies: + ethereumjs-wallet "0.6.5" + web3 "1.2.11" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +get-intrinsic@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2, glob@^7.1.3, glob@~7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +hardhat-typechain@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/hardhat-typechain/-/hardhat-typechain-0.3.5.tgz#8e50616a9da348b33bd001168c8fda9c66b7b4af" + integrity sha512-w9lm8sxqTJACY+V7vijiH+NkPExnmtiQEjsV9JKD1KgMdVk2q8y+RhvU/c4B7+7b1+HylRUCxpOIvFuB3rE4+w== + +hardhat-watcher@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/hardhat-watcher/-/hardhat-watcher-2.1.1.tgz#8b05fec429ed45da11808bbf6054a90f3e34c51a" + integrity sha512-zilmvxAYD34IofBrwOliQn4z92UiDmt2c949DW4Gokf0vS0qk4YTfVCi/LmUBICThGygNANE3WfnRTpjCJGtDA== + dependencies: + chokidar "^3.4.3" + +hardhat@^2.6.8: + version "2.6.8" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.6.8.tgz#9ef6f8c16f9044acb95609d15a760b89177b8181" + integrity sha512-iRVd5DgcIVV3rNXMlogOfwlXAhHp7Wy/OjjFiUhTey8Unvo6oq5+Is5ANiKVN+Iw07Pcb/HpkGt7jCB6a4ITgg== + dependencies: + "@ethereumjs/block" "^3.4.0" + "@ethereumjs/blockchain" "^5.4.0" + "@ethereumjs/common" "^2.4.0" + "@ethereumjs/tx" "^3.3.0" + "@ethereumjs/vm" "^5.5.2" + "@ethersproject/abi" "^5.1.2" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.14.0" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + eth-sig-util "^2.5.2" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.1.0" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + https-proxy-agent "^5.0.0" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "^4.2.0" + mnemonist "^0.38.0" + mocha "^7.1.2" + node-fetch "^2.6.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + uuid "^3.3.2" + ws "^7.4.6" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + dependencies: + has-symbol-support-x "^1.4.1" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3, has@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + +heap@0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" + +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + +immutable@^4.0.0-rc.12: + version "4.0.0-rc.12" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + dependencies: + fp-ts "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + dependencies: + call-bind "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + +is-negative-zero@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.4, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + dependencies: + has-symbols "^1.0.1" + +is-regex@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + dependencies: + has "^1.0.3" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-svg@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.1.tgz#8c63ec8c67c8c7f0a8de0a71c8c7d58eccf4406b" + integrity sha512-h2CGs+yPUyvkgTJQS9cJzo9lYK06WgRiXUqBBHtglSzVKAuH4/oWsqk7LGfbSa1hGk9QcZ0SyQtVggvBA8LZXA== + dependencies: + fast-xml-parser "^3.19.0" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + dependencies: + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.5.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.5.2, jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + +json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" + dependencies: + async "^2.0.1" + babel-preset-env "^1.7.0" + babelify "^7.3.0" + json-rpc-error "^2.0.0" + promise-to-callback "^1.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-error@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02" + dependencies: + inherits "^2.0.1" + +json-rpc-random-id@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@3.0.1, keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + dependencies: + graceful-fs "^4.1.11" + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + optionalDependencies: + graceful-fs "^4.1.9" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + dependencies: + buffer "^5.6.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + dependencies: + errno "~0.1.1" + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + dependencies: + errno "~0.1.1" + +level-iterator-stream@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz#ccfff7c046dcf47955ae9a86f46dfa06a31688b4" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" + xtend "^4.0.0" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-iterator-stream@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz#2c98a4f8820d87cdacab3132506815419077c730" + dependencies: + inherits "^2.0.1" + readable-stream "^2.3.6" + xtend "^4.0.0" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-mem@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" + dependencies: + level-packager "~4.0.0" + memdown "~3.0.0" + +level-mem@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" + integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== + dependencies: + level-packager "^5.0.3" + memdown "^5.0.0" + +level-packager@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-packager@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" + dependencies: + encoding-down "~5.0.0" + levelup "^3.0.0" + +level-post@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/level-post/-/level-post-1.0.7.tgz#19ccca9441a7cc527879a0635000f06d5e8f27d0" + dependencies: + ltgt "^2.1.2" + +level-sublevel@6.6.4: + version "6.6.4" + resolved "https://registry.yarnpkg.com/level-sublevel/-/level-sublevel-6.6.4.tgz#f7844ae893919cd9d69ae19d7159499afd5352ba" + dependencies: + bytewise "~1.1.0" + level-codec "^9.0.0" + level-errors "^2.0.0" + level-iterator-stream "^2.0.3" + ltgt "~2.1.1" + pull-defer "^0.2.2" + pull-level "^2.0.3" + pull-stream "^3.6.8" + typewiselite "~1.0.0" + xtend "~4.0.0" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +level-ws@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-1.0.0.tgz#19a22d2d4ac57b18cc7c6ecc4bd23d899d8f603b" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.8" + xtend "^4.0.1" + +level-ws@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" + integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== + dependencies: + inherits "^2.0.3" + readable-stream "^3.1.0" + xtend "^4.0.1" + +levelup@3.1.1, levelup@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" + dependencies: + deferred-leveldown "~4.0.0" + level-errors "~2.0.0" + level-iterator-stream "~3.0.0" + xtend "~4.0.0" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + dependencies: + p-locate "^4.1.0" + +lodash.assign@^4.0.3, lodash.assign@^4.0.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash@4.17.20, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + dependencies: + chalk "^2.4.2" + +looper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec" + +looper@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749" + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + +lru-cache@5.1.1, lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + dependencies: + yallist "^3.0.2" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + dependencies: + pseudomap "^1.0.1" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + dependencies: + yallist "^4.0.0" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + +ltgt@^2.1.2, ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + +ltgt@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +mcl-wasm@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.6.tgz#c1789ebda5565d49b77d2ee195ff3e4d282f1554" + integrity sha512-cbRl3sUOkBeRY2hsM4t1EIln2TIdQBkSiTOqNTv/4Hu5KOECnMWCgjIf+a9Ebunyn22VKqkMF3zj6ejRzz7YBw== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memdown@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" + integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== + dependencies: + abstract-leveldown "~6.2.1" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.2.0" + +memdown@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" + dependencies: + abstract-leveldown "~5.0.0" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + +merkle-patricia-tree@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" + dependencies: + async "^2.6.1" + ethereumjs-util "^5.2.0" + level-mem "^3.0.1" + level-ws "^1.0.0" + readable-stream "^3.0.6" + rlp "^2.0.0" + semaphore ">=1.0.1" + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +merkle-patricia-tree@^4.2.0, merkle-patricia-tree@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.2.tgz#6dec17855370172458244c2f42c989dd60b773a3" + integrity sha512-eqZYNTshcYx9aESkSPr71EqwsR/QmpnObDEV4iLxkt/x/IoLYZYjJvKY72voP/27Vy61iMOrfOG6jrn7ttXD+Q== + dependencies: + "@types/levelup" "^4.3.0" + ethereumjs-util "^7.1.2" + level-mem "^5.0.1" + level-ws "^2.0.0" + readable-stream "^3.6.0" + rlp "^2.2.4" + semaphore-async-await "^1.5.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + +mkdirp@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" + dependencies: + minimist "^1.2.5" + +mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + dependencies: + minimist "^1.2.5" + +mnemonist@^0.38.0: + version "0.38.3" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" + integrity sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw== + dependencies: + obliterator "^1.6.1" + +mocha-chai-jest-snapshot@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/mocha-chai-jest-snapshot/-/mocha-chai-jest-snapshot-1.1.1.tgz#7e49f20d0c12e6792d7f7da2e4ee0c38950571cc" + dependencies: + "@jest/test-result" "^26.5.2" + chalk "^4.1.0" + find-package-json "^1.2.0" + jest-snapshot "^26.5.2" + jest-util "^26.5.2" + slash "^3.0.0" + yargs "^16.0.3" + +mocha@^6.2.2: + version "6.2.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912" + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" + minimatch "3.0.4" + mkdirp "0.5.4" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mocha@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mock-fs@^4.1.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + +node-fetch@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + +object-assign@^4, object-assign@^4.0.0, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + +object-inspect@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + +object-is@^1.0.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.assign@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz#0dfda8d108074d9c563e80490c883b6661091544" + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +obliterator@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3" + integrity sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig== + +oboe@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + dependencies: + p-limit "^2.2.0" + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +patch-package@6.2.2, patch-package@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.2.2.tgz#71d170d650c65c26556f0d0fbbb48d92b6cc5f39" + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^1.2.1" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + +pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.0.0-beta.10: + version "1.0.0-beta.10" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.10.tgz#f2a249002733826b08d981b599335ddb7e93af8d" + integrity sha512-55UsEbeJfqYKB3RFR7Nvpi+ApEoUfgdKHVg2ZybrbOkRW4RTblyONLL3mEr8Vrxpo7wBbObVLbWodGg4YXIQ7g== + dependencies: + "@solidity-parser/parser" "^0.12.1" + dir-to-object "^2.0.0" + emoji-regex "^9.2.2" + escape-string-regexp "^4.0.0" + prettier "^2.2.1" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.2" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + +prettier@^2.0.5, prettier@^2.1.2, prettier@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + +pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +printj@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pull-cat@^1.1.9: + version "1.1.11" + resolved "https://registry.yarnpkg.com/pull-cat/-/pull-cat-1.1.11.tgz#b642dd1255da376a706b6db4fa962f5fdb74c31b" + +pull-defer@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/pull-defer/-/pull-defer-0.2.3.tgz#4ee09c6d9e227bede9938db80391c3dac489d113" + +pull-level@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pull-level/-/pull-level-2.0.4.tgz#4822e61757c10bdcc7cf4a03af04c92734c9afac" + dependencies: + level-post "^1.0.7" + pull-cat "^1.1.9" + pull-live "^1.0.1" + pull-pushable "^2.0.0" + pull-stream "^3.4.0" + pull-window "^2.1.4" + stream-to-pull-stream "^1.7.1" + +pull-live@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pull-live/-/pull-live-1.0.1.tgz#a4ecee01e330155e9124bbbcf4761f21b38f51f5" + dependencies: + pull-cat "^1.1.9" + pull-stream "^3.4.0" + +pull-pushable@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pull-pushable/-/pull-pushable-2.2.0.tgz#5f2f3aed47ad86919f01b12a2e99d6f1bd776581" + +pull-stream@^3.2.3, pull-stream@^3.4.0, pull-stream@^3.6.8: + version "3.6.14" + resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.14.tgz#529dbd5b86131f4a5ed636fdf7f6af00781357ee" + +pull-window@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/pull-window/-/pull-window-2.1.4.tgz#fc3b86feebd1920c7ae297691e23f705f88552f0" + dependencies: + looper "^2.0.0" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + +qs@^6.7.0: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-is@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + dependencies: + picomatch "^2.0.4" + +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + dependencies: + picomatch "^2.2.1" + +regenerate@^1.2.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@1.17.0, resolve@~1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + dependencies: + path-parse "^1.0.6" + +resolve@^1.10.0, resolve@^1.18.1, resolve@^1.8.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +resumer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" + dependencies: + through "~2.3.4" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + dependencies: + glob "^7.1.3" + +rimraf@^2.2.8, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + dependencies: + bn.js "^4.11.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + +rxjs@^6.4.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + dependencies: + events "^3.0.0" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + +scryptsy@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" + dependencies: + pbkdf2 "^3.0.3" + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +seedrandom@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.1.tgz#eb3dde015bcf55df05a233514e5df44ef9dce083" + +semaphore-async-await@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" + integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= + +semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + +semver@^7.3.2: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + dependencies: + lru-cache "^6.0.0" + +semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solc@^0.4.20: + version "0.4.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.26.tgz#5390a62a99f40806b86258c737c1cf653cc35cb5" + dependencies: + fs-extra "^0.30.0" + memorystream "^0.3.1" + require-from-string "^1.1.0" + semver "^5.3.0" + yargs "^4.7.1" + +solc@^0.6.3: + version "0.6.12" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.6.12.tgz#48ac854e0c729361b22a7483645077f58cba080e" + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + dependencies: + prettier-linter-helpers "^1.0.0" + +solhint@^3.2.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.2.tgz#ebd7270bb50fd378b427d7a6fc9f2a7fd00216c0" + dependencies: + "@solidity-parser/parser" "^0.8.2" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map-support@^0.5.13, source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-utils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + dependencies: + escape-string-regexp "^2.0.0" + +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + dependencies: + type-fest "^0.7.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + +stream-to-pull-stream@^1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz#4161aa2d2eb9964de60bfa1af7feaf917e874ece" + dependencies: + looper "^3.0.0" + pull-stream "^3.2.3" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string-width@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trim@~1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz#d23a22fde01c1e6571a7fadcb9be11decd8061a7" + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + +string.prototype.trimend@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@2.0.1, strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + dependencies: + has-flag "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + dependencies: + has-flag "^4.0.0" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tape@^4.6.3: + version "4.13.3" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.3.tgz#51b3d91c83668c7a45b1a594b607dee0a0b46278" + dependencies: + deep-equal "~1.1.1" + defined "~1.0.0" + dotignore "~0.1.2" + for-each "~0.3.3" + function-bind "~1.1.1" + glob "~7.1.6" + has "~1.0.3" + inherits "~2.0.4" + is-regex "~1.0.5" + minimist "~1.2.5" + object-inspect "~1.7.0" + resolve "~1.17.0" + resumer "~0.0.0" + string.prototype.trim "~1.2.1" + through "~2.3.8" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +test-value@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" + dependencies: + array-back "^1.0.3" + typical "^2.6.0" + +testrpc@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6, through@~2.3.4, through@~2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + +tmp@0.0.33, tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +tmp@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + dependencies: + rimraf "^2.6.3" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +"true-case-path@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" + +ts-essentials@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" + +ts-essentials@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.1.tgz#d205508cae0cdadfb73c89503140cf2228389e2d" + +ts-generator@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ts-generator/-/ts-generator-0.1.1.tgz#af46f2fb88a6db1f9785977e9590e7bcd79220ab" + dependencies: + "@types/mkdirp" "^0.5.2" + "@types/prettier" "^2.1.1" + "@types/resolve" "^0.0.8" + chalk "^2.4.1" + glob "^7.1.2" + mkdirp "^0.5.1" + prettier "^2.1.2" + resolve "^1.8.1" + ts-essentials "^1.0.0" + +ts-node@^8.5.4: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +tslib@^1.9.0, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +tweetnacl@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + +typechain@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-4.0.1.tgz#b40eaf5ede15588d97a4b9a5f85120f7ea1cf262" + dependencies: + command-line-args "^4.0.7" + debug "^4.1.1" + fs-extra "^7.0.0" + js-sha3 "^0.8.0" + lodash "^4.17.15" + ts-essentials "^7.0.1" + ts-generator "^0.1.1" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +typescript@^3.7.3: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + +typewise-core@^1.2, typewise-core@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195" + +typewise@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typewise/-/typewise-1.0.3.tgz#1067936540af97937cc5dcf9922486e9fa284651" + dependencies: + typewise-core "^1.2.0" + +typewiselite@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typewiselite/-/typewiselite-1.0.0.tgz#c8882fa1bb1092c06005a97f34ef5c8508e3664e" + +typical@^2.6.0, typical@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + +underscore@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + +unorm@^1.3.3: + version "1.6.0" + resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + +utf-8-validate@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.3.tgz#3b64e418ad2ff829809025fdfef595eab2f03a27" + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util.promisify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" + +web3-bzz@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.11.tgz#41bc19a77444bd5365744596d778b811880f707f" + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.9.1" + +web3-core-helpers@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz#84c681ed0b942c0203f3b324a245a127e8c67a99" + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.11" + web3-utils "1.2.11" + +web3-core-method@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.11.tgz#f880137d1507a0124912bf052534f168b8d8fbb6" + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.9.1" + web3-core-helpers "1.2.11" + web3-core-promievent "1.2.11" + web3-core-subscriptions "1.2.11" + web3-utils "1.2.11" + +web3-core-promievent@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz#51fe97ca0ddec2f99bf8c3306a7a8e4b094ea3cf" + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz#fe6eb603fbaee18530293a91f8cf26d8ae28c45a" + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.11" + web3-providers-http "1.2.11" + web3-providers-ipc "1.2.11" + web3-providers-ws "1.2.11" + +web3-core-subscriptions@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz#beca908fbfcb050c16f45f3f0f4c205e8505accd" + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + +web3-core@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.11.tgz#1043cacc1becb80638453cc5b2a14be9050288a7" + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-requestmanager "1.2.11" + web3-utils "1.2.11" + +web3-eth-abi@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz#a887494e5d447c2926d557a3834edd66e17af9b0" + dependencies: + "@ethersproject/abi" "5.0.0-beta.153" + underscore "1.9.1" + web3-utils "1.2.11" + +web3-eth-accounts@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz#a9e3044da442d31903a7ce035a86d8fa33f90520" + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-utils "1.2.11" + +web3-eth-contract@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz#917065902bc27ce89da9a1da26e62ef663663b90" + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-promievent "1.2.11" + web3-core-subscriptions "1.2.11" + web3-eth-abi "1.2.11" + web3-utils "1.2.11" + +web3-eth-ens@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz#26d4d7f16d6cbcfff918e39832b939edc3162532" + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-promievent "1.2.11" + web3-eth-abi "1.2.11" + web3-eth-contract "1.2.11" + web3-utils "1.2.11" + +web3-eth-iban@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz#f5f73298305bc7392e2f188bf38a7362b42144ef" + dependencies: + bn.js "^4.11.9" + web3-utils "1.2.11" + +web3-eth-personal@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz#a38b3942a1d87a62070ce0622a941553c3d5aa70" + dependencies: + "@types/node" "^12.12.6" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-net "1.2.11" + web3-utils "1.2.11" + +web3-eth@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.11.tgz#4c81fcb6285b8caf544058fba3ae802968fdc793" + dependencies: + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-subscriptions "1.2.11" + web3-eth-abi "1.2.11" + web3-eth-accounts "1.2.11" + web3-eth-contract "1.2.11" + web3-eth-ens "1.2.11" + web3-eth-iban "1.2.11" + web3-eth-personal "1.2.11" + web3-net "1.2.11" + web3-utils "1.2.11" + +web3-net@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.11.tgz#eda68ef25e5cdb64c96c39085cdb74669aabbe1b" + dependencies: + web3-core "1.2.11" + web3-core-method "1.2.11" + web3-utils "1.2.11" + +web3-provider-engine@14.2.1: + version "14.2.1" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.2.1.tgz#ef351578797bf170e08d529cb5b02f8751329b95" + dependencies: + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^3.0.0" + eth-json-rpc-infura "^3.1.0" + eth-sig-util "^1.4.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +web3-providers-http@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.11.tgz#1cd03442c61670572d40e4dcdf1faff8bd91e7c6" + dependencies: + web3-core-helpers "1.2.11" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz#d16d6c9be1be6e0b4f4536c4acc16b0f4f27ef21" + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + +web3-providers-ws@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz#a1dfd6d9778d840561d9ec13dd453046451a96bb" + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + websocket "^1.0.31" + +web3-shh@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.11.tgz#f5d086f9621c9a47e98d438010385b5f059fd88f" + dependencies: + web3-core "1.2.11" + web3-core-method "1.2.11" + web3-core-subscriptions "1.2.11" + web3-net "1.2.11" + +web3-utils@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.11.tgz#af1942aead3fb166ae851a985bed8ef2c2d95a82" + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@^1.0.0-beta.31: + version "1.3.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.1.tgz#9aa880dd8c9463fe5c099107889f86a085370c2e" + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" + dependencies: + web3-bzz "1.2.11" + web3-core "1.2.11" + web3-eth "1.2.11" + web3-eth-personal "1.2.11" + web3-net "1.2.11" + web3-shh "1.2.11" + web3-utils "1.2.11" + +websocket@1.0.32: + version "1.0.32" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.32.tgz#1f16ddab3a21a2d929dec1687ab21cfdc6d3dbb1" + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +websocket@^1.0.31: + version "1.0.33" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.33.tgz#407f763fc58e74a3fa41ca3ae5d78d3f5e3b82a5" + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@1.3.1, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + dependencies: + mkdirp "^0.5.1" + +ws@7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + dependencies: + async-limiter "~1.0.0" + +ws@^7.4.6: + version "7.5.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + +yargs-parser@^20.2.2: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@^16.0.3: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^4.7.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.1" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" diff --git a/script/Deployer.s.sol b/script/Deployer.s.sol new file mode 100644 index 0000000..90be8b3 --- /dev/null +++ b/script/Deployer.s.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import {KyotoPay} from "../src/KyotoPay.sol"; +import {Constants} from "./reference/Constants.s.sol"; + +contract Deployer is Script, Constants { + + uint256 constant ETH_CHAIN_ID = 1; + uint256 constant GOERLI_CHAIN_ID = 5; + + address uniswapRouterAddress; + address wethAddress; + address usdcAddress; + address usdtAddress; + address multisigAddress; + KyotoPay public kyotoPay; + + + function _setUp() internal { + if (block.chainid == ETH_CHAIN_ID) { + uniswapRouterAddress = UNISWAP_ROUTER_ADDRESS_MAINNET; + wethAddress = WETH_ADDRESS_MAINNET; + usdcAddress = USDC_ADDRESS_MAINNET; + usdtAddress = USDT_ADDRESS_MAINNET; + multisigAddress = MAINNET_MULTISIG; + } + else if (block.chainid == GOERLI_CHAIN_ID) { + uniswapRouterAddress = UNISWAP_ROUTER_ADDRESS_GOERLI; + wethAddress = WETH_ADDRESS_GOERLI; + usdcAddress = USDC_ADDRESS_GOERLI; + usdtAddress = USDT_ADDRESS_GOERLI; + // No multisig on Goerli... + multisigAddress = GOERLI_EOA; + } + else { + revert("Unsupported chain"); + } + } + + function run() external { + _setUp(); + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + kyotoPay = new KyotoPay(100, uniswapRouterAddress, wethAddress); + + /** + * Adding inputs... + */ + kyotoPay.addToInputWhitelist(usdcAddress); + kyotoPay.addToInputWhitelist(usdtAddress); + kyotoPay.addToInputWhitelist(wethAddress); + + /** + * Adding outputs + */ + kyotoPay.addToOutputWhitelist(usdcAddress); + kyotoPay.addToOutputWhitelist(usdtAddress); + kyotoPay.addToOutputWhitelist(wethAddress); + + kyotoPay.transferOwnership(multisigAddress); + + vm.stopBroadcast(); + } +} diff --git a/script/reference/Constants.s.sol b/script/reference/Constants.s.sol new file mode 100644 index 0000000..7a41364 --- /dev/null +++ b/script/reference/Constants.s.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +abstract contract Constants { + + /** + * Goerli + */ + address constant UNISWAP_ROUTER_ADDRESS_GOERLI = 0xE592427A0AEce92De3Edee1F18E0157C05861564; + address constant WETH_ADDRESS_GOERLI = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6; + address constant USDC_ADDRESS_GOERLI = 0x07865c6E87B9F70255377e024ace6630C1Eaa37F; + // Note: there is no official Tether testnet deployment. This is a randomly selected deployment for testing + address constant USDT_ADDRESS_GOERLI = 0x509Ee0d083DdF8AC028f2a56731412edD63223B9; + address constant GOERLI_EOA = 0x8313b3727E47efaaBB90b7C2f00A73758D52A2b5; + + /** + * Mainnet + */ + address constant UNISWAP_ROUTER_ADDRESS_MAINNET = 0xE592427A0AEce92De3Edee1F18E0157C05861564; + address constant WETH_ADDRESS_MAINNET = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address constant USDC_ADDRESS_MAINNET = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address constant USDT_ADDRESS_MAINNET = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address constant MAINNET_MULTISIG = 0x9211A0BB478B4Bbed725e63D26a784A0cE19E3e4; +} \ No newline at end of file diff --git a/src/KyotoPay.sol b/src/KyotoPay.sol new file mode 100644 index 0000000..6088896 --- /dev/null +++ b/src/KyotoPay.sol @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +/// @title KyotoPay +/// Version 1.0 + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; +import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; +import {IKyotoPay} from "./interfaces/IKyotoPay.sol"; +import {IWETH9} from "./interfaces/IWETH9.sol"; + +contract KyotoPay is Ownable, Pausable, IKyotoPay { + using SafeERC20 for IERC20; + + uint256 public constant DECIMALS = 10_000; + + // MAX_ADMIN_FEE is denominated in DECIMALs. I.e. 500 = 5% + uint256 public constant MAX_ADMIN_FEE = 500; + + address public immutable uniswapSwapRouterAddress; + address public immutable wethAddress; + + // adminFee is denominated in DECIMALS. For example, a value for fee of 200 = 2% + uint256 public adminFee; + + // mapping for prferences + mapping(address => Preferences) public recipientPreferences; + mapping(address => bool) public whitelistedInputTokens; + mapping(address => bool) public whitelistedOutputTokens; + + constructor(uint256 _adminFee, address _uniswapSwapRouterAddress, address _wethAddress) { + if (_adminFee > MAX_ADMIN_FEE) revert InvalidAdminFee(); + if (_uniswapSwapRouterAddress == address(0)) revert ZeroAddress(); + if (_wethAddress == address(0)) revert ZeroAddress(); + + adminFee = _adminFee; + uniswapSwapRouterAddress = _uniswapSwapRouterAddress; + wethAddress = _wethAddress; + } + + /** + * @notice sets the sender's receiving preferences. + * @param _preferences the sender's given preferences + * Note: slippageAllowed is inversed. For example, 9_900 is 1% slippage + * Requirements: + * - '_preferences.slippageAllowed' is not 0% (i.e. >= 10,000) or 100% (i.e. 0) + * - '_preferences.tokenAddress' is a valid output token found in whitelistedOutputTokens + */ + function setPreferences(Preferences calldata _preferences) external whenNotPaused { + if ((_preferences.slippageAllowed == 0) || (_preferences.slippageAllowed >= DECIMALS)) { + revert InvalidRecipientSlippage(); + } + if (!(whitelistedOutputTokens[_preferences.tokenAddress])) revert InvalidRecipientToken(); + + recipientPreferences[msg.sender] = _preferences; + } + + /** + * @notice pays a recipient in their preferred token from a given input token + * @param _recipient the recipient to pay + * @param _tokenIn the token to send + * @param _amountIn the amount of tokens to send + * @param _amountOut estimate of the Uniswap output of recipient's preferred token. Calculated on the frontend + * @param _uniFee a Uniswap fee for a given pool + * @param _data data about the transaction to be indexed + * Requirements: + * - '_recipient' != address(0) + * - '_tokenIn' is a valid input token + * - '_amountIn' != 0 + * - 'amountOut' != 0 + * - '_uniFee' is a valid Uniswap pool fee + * - The executed swap will send the recipient more tokens than their slippageAllowed * '_amountOut' + * - The user's token balance > '_amountIn' + * - The user has approve the contract to transfer their tokens + */ + function pay( + address _recipient, + address _tokenIn, + uint256 _amountIn, + uint256 _amountOut, + uint24 _uniFee, + bytes32 _data + ) external whenNotPaused { + _validateInputParams(_recipient, _tokenIn, _amountIn, _amountOut, _uniFee); + + // transfer the amount to this contract (should fail if the contract will not allow it) + _getSenderFunds(_tokenIn, _amountIn); + + _pay(_recipient, _tokenIn, _amountIn, _amountOut, _uniFee, _data); + } + + /** + * @notice pays a recipient in their preferred token from the given ether + * @param _recipient the recipient to pay + * @param _amountOut estimate of the Uniswap output of recipient's preferred token. Calculated on the frontend + * @param _uniFee a Uniswap fee for a given pool + * @param _data data about the transaction to be indexed + * Note: if the user has not set their preferences, they will receive WETH and not ETH + * Requirements: + * - '_recipient' != address(0) + * - WETH is a whitelisted input + * - msg.value > 0 + * - 'amountOut' != 0 + * - '_uniFee' is a valid Uniswap pool fee + * - The executed swap will send the recipient more tokens than their slippageAllowed * '_amountOut' + */ + + function payEth(address _recipient, uint256 _amountOut, uint24 _uniFee, bytes32 _data) external payable whenNotPaused { + // Cache vars + uint256 _msgValue = msg.value; + address _wethAddress = wethAddress; + + _validateInputParams(_recipient, _wethAddress, _msgValue, _amountOut, _uniFee); + + IWETH9(_wethAddress).deposit{value: _msgValue}(); + + _pay(_recipient, wethAddress, _msgValue, _amountOut, _uniFee, _data); + } + + /******************************* + * + * Internal functions + * + ******************************/ + + /** + * @dev validates preferences, gets recipient funds, executes the UNI swap, sends funds to recipient + * Does not execute a UNI swap if the input token is the same as the output token or if the recipient has not set preferences + * Instead, _pay will send the user funds directly to the recipient after a fee + */ + function _pay( + address _recipient, + address _tokenIn, + uint256 _amountIn, + uint256 _amountOut, + uint24 _uniFee, + bytes32 _data + ) internal { + // Cache the recipient's preferences + Preferences memory _preferences = recipientPreferences[_recipient]; + bool areValidPreferences = _validatePreferences(_preferences); + + // If the sender's token is the recipient's preferred token or recipient's preferences haven't been set, transfer directly and stop execution + if ((_tokenIn == _preferences.tokenAddress) || !(areValidPreferences)) { + _sendRecipientFunds(_tokenIn, _recipient, _amountIn); + emit Payment(_recipient, _tokenIn, _amountIn, _data); + + return; + } + + uint256 swapOutput = _executeSwap( + _tokenIn, _preferences.tokenAddress, _amountIn, _amountOut, _uniFee, _preferences.slippageAllowed + ); + + // transfer funds to recipient (will pay the owners here too) + _sendRecipientFunds(_preferences.tokenAddress, _recipient, swapOutput); + + // emit any data for end user use + emit Payment(_recipient, _tokenIn, _amountIn, _data); + } + /** + * @dev internal function to execute a swap using the Uniswap Swap Router + * Uses the recipient's set slippage for amountOut + */ + function _executeSwap( + address _tokenIn, + address _tokenOut, + uint256 _amountIn, + uint256 _amountOut, + uint24 _uniFee, + uint96 _slippageAllowed + ) internal returns (uint256) { + IERC20(_tokenIn).safeApprove(uniswapSwapRouterAddress, _amountIn); + + // create the input params + ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ + tokenIn: _tokenIn, + tokenOut: _tokenOut, + fee: _uniFee, // e.g. fee for a pool at 0.3% tier is 3000 + recipient: address(this), // this contract will be doing the distribution of funds + deadline: block.timestamp, + amountIn: _amountIn, + amountOutMinimum: ((_amountOut * uint256(_slippageAllowed)) / DECIMALS), + sqrtPriceLimitX96: 0 // sets a limit for the price that the swap will push to the pool (setting to 0 makes it inactive) --> will require more research + }); + + // swap currency on uniswap + return ISwapRouter(uniswapSwapRouterAddress).exactInputSingle(params); + } + /** + * @dev Internal function to validate input parameters. Reverts if given invalid input params. + * Note: Uniswap fees for pools are 0.01%, 0.05%, 0.30%, and 1.00% + * They are represented in hundredths of basis points. I.e. 100 = 0.01%, 500 = 0.05%, etc. + */ + + function _validateInputParams( + address _recipient, + address _tokenIn, + uint256 _amountIn, + uint256 _amountOut, + uint24 _uniFee + ) internal view { + require(((_uniFee == 100) || (_uniFee == 500) || (_uniFee == 3000) || (_uniFee == 10_000)), "Invalid Uni Fee"); + if (!(whitelistedInputTokens[_tokenIn])) revert InvalidToken(); + if (_recipient == address(0)) revert ZeroAddress(); + if (_amountIn == 0 || _amountOut == 0) revert InvalidAmount(); + } + + /** + * @dev validates recipient's preferences. Does not revert. + * @return true when valid preferences, false when invalid + */ + function _validatePreferences(Preferences memory _preferences) internal view returns (bool) { + return ((_preferences.slippageAllowed != 0) && (whitelistedOutputTokens[_preferences.tokenAddress])); + } + + /** + * @dev safe transfers funds from the user to address(this) + */ + function _getSenderFunds(address _tokenAddress, uint256 _amountIn) internal { + IERC20(_tokenAddress).safeTransferFrom(msg.sender, address(this), _amountIn); + } + + /** + * @dev safeTransfer tokens to a given recipient given a ERC20 token address and amount to send + */ + function _sendRecipientFunds(address _tokenAddress, address _recipient, uint256 _amount) internal { + // calculate the owner payment, this amount will stay in the contract and can be withdrawn at will (no reason to make superfluous transfers) + uint256 ownerPayment = (_amount * adminFee) / DECIMALS; + + // pay the recipient the excess + IERC20(_tokenAddress).safeTransfer(_recipient, _amount - ownerPayment); + } + + /******************************* + * + * Admin functions + * + ******************************/ + + /** + * @dev Admin function to set the fee + * @param _adminFee the new fee amount + * Requirements: + * - 'adminFee" <= 'MAX_ADMIN_FEE' + * - msg.sender is the owner + */ + function setAdminFee(uint256 _adminFee) external onlyOwner { + if (_adminFee > MAX_ADMIN_FEE) revert InvalidAdminFee(); + adminFee = _adminFee; + } + + /** + * @dev Admin function to add a token to the input whitelist + * @param _token the address of the token + * Requirements: + * - '_token" != address(0) + * - msg.sender is the owner + */ + function addToInputWhitelist(address _token) external onlyOwner { + if (_token == address(0)) revert ZeroAddress(); + whitelistedInputTokens[_token] = true; + } + + /** + * @dev Admin function to revoke a token from the input whitelist + * @param _token the address of the token + * Requirements: + * - '_token" != address(0) + * - msg.sender is the owner + */ + function revokeFromInputWhitelist(address _token) external onlyOwner { + if (_token == address(0)) revert ZeroAddress(); + delete whitelistedInputTokens[_token]; + } + + /** + * @dev Admin function to add a token to the output whitelist + * @param _token the address of the token + * Requirements: + * - '_token" != address(0) + * - msg.sender is the owner + */ + function addToOutputWhitelist(address _token) external onlyOwner { + if (_token == address(0)) revert ZeroAddress(); + whitelistedOutputTokens[_token] = true; + } + + /** + * @dev Admin function to revoke a token from the output whitelist + * @param _token the address of the token + * Requirements: + * - '_token" != address(0) + * - msg.sender is the owner + */ + function revokeFromOutputWhitelist(address _token) external onlyOwner { + if (_token == address(0)) revert ZeroAddress(); + delete whitelistedOutputTokens[_token]; + } + + /** + * @dev Admin function to withdraw tokens from a given token address + * Note: '_token' is not validated before passing it in as an argument + * '_token' must always be verified manually before being called by the admin + * @param _token the address of the token to withdraw + * @param _amount the amount of token to withdraw + * Requirements: + * Requirements: + * - '_token" != address(0) + * - msg.sender is the owner + * - Token balance of address(this) > 0 + */ + function withdraw(address _token, uint256 _amount) external onlyOwner { + if (IERC20(_token).balanceOf(address(this)) == 0) revert ZeroBalance(); + IERC20(_token).safeTransfer(msg.sender, _amount); + } + + /** + * @dev Admin function to pause payments + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @dev Admin function to unpause payments + */ + function unpause() external onlyOwner { + _unpause(); + } +} diff --git a/src/interfaces/IKyotoPay.sol b/src/interfaces/IKyotoPay.sol new file mode 100644 index 0000000..a7b327e --- /dev/null +++ b/src/interfaces/IKyotoPay.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +/// @title Interface for KyotoPay +/// Version 1.0 + +interface IKyotoPay { + + struct Preferences { + address tokenAddress; + uint96 slippageAllowed; + } + + /** + * Invalid value(s) for arguments 'amountIn' or '_amountOut'. '_amountIn' is zero or '_amountOut' is zero + */ + error InvalidAmount(); + + /** + * Argument '_fee' in setFee cannot be greater than MAX_FEE + */ + error InvalidAdminFee(); + + /** + * Argument '_tokenIn' is not a valid input token + */ + error InvalidToken(); + + /** + * Argument '_preferences.slippageAllowed' is invalid: it is zero or greater than the decimal values + */ + error InvalidRecipientSlippage(); + + /** + * Argument '_preferences.tokenAddress' is not a valid whitelisted output token found in 'whitelistedOutputTokens' + */ + error InvalidRecipientToken(); + + /** + * Passed in address is address(0) + */ + error ZeroAddress(); + + /** + * address(this) has a token balance of 0 for the passed in '_tokenAddress" + */ + error ZeroBalance(); + + /** + * Emitted to pass data from payment function + */ + event Payment(address recipient, address indexed tokenAddress, uint256 indexed amountIn, bytes32 indexed data); + + /** + * Sets the sender's receiving preferences. + * Note: slippageAllowed is inversed. For example, 9_900 is 1% slippage + * Requirements: + * - '_preferences.slippageAllowed' is not 0% (i.e. >= 10,000) or 100% (i.e. 0) + * - '_preferences.tokenAddress' is a valid output token found in whitelistedOutputTokens + */ + function setPreferences(Preferences calldata _preferences) external; + + /** + * Pays a recipient in their preferred token from a given input token + * Requirements: + * - '_recipient' != address(0) + * - '_tokenIn' is a valid input token + * - '_amountIn' != 0 + * - 'amountOut' != 0 + * - '_uniFee' is a valid Uniswap pool fee + * - The executed swap will send the recipient more tokens than their slippageAllowed * '_amountOut' + * - The user's token balance > '_amountIn' + * - The user has approve the contract to transfer their tokens + */ + function pay( address _recipient, address _tokenIn, uint256 _amountIn, uint256 _amountOut, uint24 _uniFee, bytes32 _data) external; + + /** + * Pays a recipient in their preferred token from the given ether + * Note: if the user has not set their preferences, they will receive WETH and not ETH + * Requirements: + * - '_recipient' != address(0) + * - WETH is a whitelisted input + * - msg.value > 0 + * - 'amountOut' != 0 + * - '_uniFee' is a valid Uniswap pool fee + * - The executed swap will send the recipient more tokens than their slippageAllowed * '_amountOut' + */ + + function payEth(address _recipient, uint256 _amountOut, uint24 _uniFee, bytes32 _data) external payable; +} diff --git a/src/interfaces/IWETH9.sol b/src/interfaces/IWETH9.sol new file mode 100644 index 0000000..92bcc55 --- /dev/null +++ b/src/interfaces/IWETH9.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/// @title Interface for WETH9 +interface IWETH9 is IERC20 { + // Deposit ether to get wrapped ether + function deposit() external payable; + + // Withdraw wrapped ether to get ether + function withdraw(uint256) external; +} diff --git a/test/Deployer.t.sol b/test/Deployer.t.sol new file mode 100644 index 0000000..c4a0df1 --- /dev/null +++ b/test/Deployer.t.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {Deployer} from "../script/Deployer.s.sol"; +import {Constants} from "../script/reference/Constants.s.sol"; +import {KyotoPay} from "../src/KyotoPay.sol"; +import {IKyotoPay} from "../src/interfaces/IKyotoPay.sol"; +import {Fork} from "./reference/Fork.sol"; +contract DeployerTest is Test, Fork, Constants { + + Deployer deployer; + + function testFork_DeploymentMainnet() public { + vm.createSelectFork(MAINNET_RPC_URL, MAINNET_FORK_BLOCK); + + deployer = new Deployer(); + deployer.run(); + + KyotoPay _kyotoPay = KyotoPay(deployer.kyotoPay()); + + assertEq(_kyotoPay.uniswapSwapRouterAddress(), UNISWAP_ROUTER_ADDRESS_MAINNET); + assertEq(_kyotoPay.wethAddress(), WETH_ADDRESS_MAINNET); + + assertTrue(_kyotoPay.whitelistedInputTokens(USDC_ADDRESS_MAINNET)); + assertTrue(_kyotoPay.whitelistedInputTokens(USDT_ADDRESS_MAINNET)); + assertTrue(_kyotoPay.whitelistedInputTokens(WETH_ADDRESS_MAINNET)); + + assertTrue(_kyotoPay.whitelistedOutputTokens(USDC_ADDRESS_MAINNET)); + assertTrue(_kyotoPay.whitelistedOutputTokens(USDT_ADDRESS_MAINNET)); + assertTrue(_kyotoPay.whitelistedOutputTokens(WETH_ADDRESS_MAINNET)); + + assertEq(_kyotoPay.owner(), MAINNET_MULTISIG); + } + function testFork_DeploymentGoerli() public { + vm.createSelectFork(GOERLI_RPC_URL, GOERLI_FORK_BLOCK); + + deployer = new Deployer(); + deployer.run(); + + KyotoPay _kyotoPay = KyotoPay(deployer.kyotoPay()); + + assertEq(_kyotoPay.uniswapSwapRouterAddress(), UNISWAP_ROUTER_ADDRESS_GOERLI); + assertEq(_kyotoPay.wethAddress(), WETH_ADDRESS_GOERLI); + + assertTrue(_kyotoPay.whitelistedInputTokens(USDC_ADDRESS_GOERLI)); + assertTrue(_kyotoPay.whitelistedInputTokens(USDT_ADDRESS_GOERLI)); + assertTrue(_kyotoPay.whitelistedInputTokens(WETH_ADDRESS_GOERLI)); + + assertTrue(_kyotoPay.whitelistedOutputTokens(USDC_ADDRESS_GOERLI)); + assertTrue(_kyotoPay.whitelistedOutputTokens(USDT_ADDRESS_GOERLI)); + assertTrue(_kyotoPay.whitelistedOutputTokens(WETH_ADDRESS_GOERLI)); + + assertEq(_kyotoPay.owner(), GOERLI_EOA); + } + + function testFork_Deployment_RevertIf_UnsupportedChain() public { + vm.createSelectFork(POLYGON_RPC_URL, POLYGON_FORK_BLOCK); + + deployer = new Deployer(); + vm.expectRevert("Unsupported chain"); + deployer.run(); + } +} \ No newline at end of file diff --git a/test/KyotoPay.t.sol b/test/KyotoPay.t.sol new file mode 100644 index 0000000..8e2b094 --- /dev/null +++ b/test/KyotoPay.t.sol @@ -0,0 +1,1138 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {KyotoPay} from "../src/KyotoPay.sol"; +import {IKyotoPay} from "../src/interfaces/IKyotoPay.sol"; +import {IWETH9} from "../src/interfaces/IWETH9.sol"; +import {Fork} from "./reference/Fork.sol"; +import {Helper} from "./reference/Helper.sol"; +import {KyotoPayWrapper} from "./reference/KyotoPayWrapper.sol"; +import {MockERC20} from "./reference/MockERC20.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; + +/************************* + * + * If you're unfamiliar with Foundry best practices, + * read the following documentation before procceeding: https://book.getfoundry.sh/tutorials/best-practices + * + **************************/ + +contract Constructor is Test, Helper { + KyotoPay kyotoPayContract; + + function test_RevertIf_InvalidAdminFee() public { + uint256 _invalidAdminFee = 600; + vm.expectRevert(IKyotoPay.InvalidAdminFee.selector); + kyotoPayContract = new KyotoPay(_invalidAdminFee, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + } + + function test_RevertIf_UniswapRouterZeroAddress() public { + uint256 _validAdminFee = 100; + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPayContract = new KyotoPay(_validAdminFee, address(0), WETH_ADDRESS); + } + + function test_RevertIf_WethZeroAddress() public { + uint256 _validAdminFee = 100; + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPayContract = new KyotoPay(_validAdminFee, UNISWAP_SWAPROUTER_ADDRESS, address(0)); + } + + function test_ValidParams() public { + kyotoPayContract = new KyotoPay(FEE, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + + assertEq(kyotoPayContract.adminFee(), FEE); + assertEq(kyotoPayContract.uniswapSwapRouterAddress(), UNISWAP_SWAPROUTER_ADDRESS); + } +} + +contract Setters is Test, Helper { + using SafeERC20 for ERC20; + + KyotoPay kyotoPayContract; + address mockERC20; + + function setUp() public { + kyotoPayContract = new KyotoPay(FEE, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + mockERC20 = address(new MockERC20()); + kyotoPayContract.addToInputWhitelist(mockERC20); + kyotoPayContract.addToOutputWhitelist(mockERC20); + } + + function test_SetPreferences() public { + uint96 _validSlippage = 100; + + IKyotoPay.Preferences memory _validPreferences = + IKyotoPay.Preferences({tokenAddress: mockERC20, slippageAllowed: _validSlippage}); + + vm.prank(RANDOM_USER); + kyotoPayContract.setPreferences(_validPreferences); + + (address _userTokenAddress, uint96 _userSlippageAllowed) = kyotoPayContract.recipientPreferences(RANDOM_USER); + + assertEq(_userTokenAddress, mockERC20); + assertEq(_userSlippageAllowed, _validSlippage); + } + + function test_SetPreferences_RevertIf_SlippagePreferenceZero() public { + IKyotoPay.Preferences memory _invalidSlippage = + IKyotoPay.Preferences({tokenAddress: mockERC20, slippageAllowed: 0}); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidRecipientSlippage.selector); + kyotoPayContract.setPreferences(_invalidSlippage); + + vm.stopPrank(); + } + + function test_SetPreferences_RevertIf_SlippagePreferenceEqualToDecimals() public { + uint256 decimals256 = kyotoPayContract.DECIMALS(); + uint96 invalidSlippage = uint96(decimals256); + + IKyotoPay.Preferences memory _invalidSlippage = + IKyotoPay.Preferences({tokenAddress: mockERC20, slippageAllowed: invalidSlippage}); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidRecipientSlippage.selector); + kyotoPayContract.setPreferences(_invalidSlippage); + + vm.stopPrank(); + } + + function test_SetPreferences_RevertIf_SlippagePreferenceGreaterThanDecimals() public { + uint256 decimals256 = kyotoPayContract.DECIMALS(); + uint96 invalidSlippage = uint96(decimals256 + 1); + + IKyotoPay.Preferences memory _invalidSlippage = + IKyotoPay.Preferences({tokenAddress: mockERC20, slippageAllowed: invalidSlippage}); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidRecipientSlippage.selector); + kyotoPayContract.setPreferences(_invalidSlippage); + + vm.stopPrank(); + } + + /** + * @dev DAI hasn't been added to whitelisted tokens yet + */ + function test_SetPreference_RevertIf_InvalidTokenPreference() public { + assertFalse(kyotoPayContract.whitelistedOutputTokens(DAI_ADDRESS)); + + IKyotoPay.Preferences memory _invalidToken = + IKyotoPay.Preferences({tokenAddress: DAI_ADDRESS, slippageAllowed: 100}); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidRecipientToken.selector); + kyotoPayContract.setPreferences(_invalidToken); + } + + function test_Pause_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.pause(); + + vm.stopPrank(); + } + + function test_Unpause_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.unpause(); + + vm.stopPrank(); + } + + function test_Pause() public { + vm.prank(ADMIN); + kyotoPayContract.pause(); + } + + function test_Unpause() public { + vm.startPrank(ADMIN); + + kyotoPayContract.pause(); + kyotoPayContract.unpause(); + + vm.stopPrank(); + } + + function test_SetAdminFee() public { + uint256 _validFee = 200; + + vm.prank(ADMIN); + kyotoPayContract.setAdminFee(_validFee); + + assertEq(kyotoPayContract.adminFee(), _validFee); + } + + function test_SetAdminFee_RevertIf_GreaterThanMaxFee() public { + uint256 _maxFee = kyotoPayContract.MAX_ADMIN_FEE(); + + vm.startPrank(ADMIN); + + vm.expectRevert(IKyotoPay.InvalidAdminFee.selector); + kyotoPayContract.setAdminFee(_maxFee + 1); + + vm.stopPrank(); + } + + function test_SetAdminFee_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.setAdminFee(200); + + vm.stopPrank(); + } +} + +contract Whitelist is Test, Helper { + using SafeERC20 for ERC20; + + KyotoPay kyotoPayContract; + address mockERC20; + + function setUp() public { + kyotoPayContract = new KyotoPay(FEE, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + mockERC20 = address(new MockERC20()); + } + + function test_addToInputWhitelist() public { + vm.prank(ADMIN); + kyotoPayContract.addToInputWhitelist(mockERC20); + assertTrue(kyotoPayContract.whitelistedInputTokens(mockERC20)); + } + + function test_addToOutputWhitelist() public { + vm.prank(ADMIN); + kyotoPayContract.addToOutputWhitelist(mockERC20); + assertTrue(kyotoPayContract.whitelistedOutputTokens(mockERC20)); + } + + function test_addToInputWhiteList_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.addToInputWhitelist(mockERC20); + + vm.stopPrank(); + } + + function test_addToOutputWhitelist_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.addToOutputWhitelist(mockERC20); + + vm.stopPrank(); + } + + function test_addToInputWhiteList_RevertIf_ZeroAddress() public { + vm.startPrank(ADMIN); + + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPayContract.addToInputWhitelist(address(0)); + + vm.stopPrank(); + } + + function test_addToOutputWhiteList_RevertIf_ZeroAddress() public { + vm.startPrank(ADMIN); + + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPayContract.addToOutputWhitelist(address(0)); + + vm.stopPrank(); + } + + function test_revokeFromInputWhitelist() public { + vm.startPrank(ADMIN); + + kyotoPayContract.addToInputWhitelist(mockERC20); + assertTrue(kyotoPayContract.whitelistedInputTokens(mockERC20)); + + kyotoPayContract.revokeFromInputWhitelist(mockERC20); + assertFalse(kyotoPayContract.whitelistedInputTokens(mockERC20)); + } + + function test_revokeFromOutputWhitelist() public { + vm.startPrank(ADMIN); + + kyotoPayContract.addToOutputWhitelist(mockERC20); + assertTrue(kyotoPayContract.whitelistedOutputTokens(mockERC20)); + + kyotoPayContract.revokeFromOutputWhitelist(mockERC20); + assertFalse(kyotoPayContract.whitelistedOutputTokens(mockERC20)); + } + + function test_revokeFromInputWhiteList_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.revokeFromInputWhitelist(mockERC20); + + vm.stopPrank(); + } + + function test_revokeFromOutputWhiteList_RevertIf_NotOwner() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPayContract.revokeFromOutputWhitelist(mockERC20); + + vm.stopPrank(); + } + + function test_revokeFromInputWhiteListRevertIf_ZeroAddress() public { + vm.startPrank(ADMIN); + + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPayContract.revokeFromInputWhitelist(address(0)); + + vm.stopPrank(); + } + + function test_revokeFromOutputWhiteListRevertIf_ZeroAddress() public { + vm.startPrank(ADMIN); + + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPayContract.revokeFromOutputWhitelist(address(0)); + + vm.stopPrank(); + } +} + +contract InternalFunctions is Test, Helper { + using SafeERC20 for ERC20; + + KyotoPayWrapper kyotoPayWrapper; + ERC20 mockERC20; + + function setUp() public { + kyotoPayWrapper = new KyotoPayWrapper(FEE, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + mockERC20 = ERC20(new MockERC20()); + kyotoPayWrapper.addToInputWhitelist(address(mockERC20)); + kyotoPayWrapper.addToOutputWhitelist(address(mockERC20)); + } + + function _transferMockERC20(address _recipient, uint256 _amount) internal { + mockERC20.safeTransfer(_recipient, _amount); + } + + function test_validatePreferences() public { + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: address(mockERC20), slippageAllowed: 100}); + + assertTrue(kyotoPayWrapper.validatePreferences(_preferences)); + } + + function test_validatePreferences_RevertIf_SlippageZero() public { + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: address(mockERC20), slippageAllowed: 0}); + + assertFalse(kyotoPayWrapper.validatePreferences(_preferences)); + } + + function test_validatePreferences_RevertIf_TokenNotWhitelisted() public { + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: USDC_ADDRESS, slippageAllowed: 100}); + + assertFalse(kyotoPayWrapper.validatePreferences(_preferences)); + } + + function test_getSenderFunds() public { + uint256 _toSend = 1_000 ether; + + _transferMockERC20(RANDOM_USER, _toSend); + + assertEq(mockERC20.balanceOf(RANDOM_USER), _toSend); + + vm.startPrank(RANDOM_USER); + + mockERC20.safeApprove(address(kyotoPayWrapper), _toSend); + kyotoPayWrapper.getSenderFunds(address(mockERC20), _toSend); + + vm.stopPrank(); + + assertEq(mockERC20.balanceOf(address(kyotoPayWrapper)), _toSend); + assertEq(mockERC20.balanceOf(RANDOM_USER), 0); + assertEq(mockERC20.allowance(RANDOM_USER, address(kyotoPayWrapper)), 0); + } + + function test_sendRecipientFunds() public { + uint256 _fee = kyotoPayWrapper.adminFee(); + uint256 _decimals = kyotoPayWrapper.DECIMALS(); + uint256 _toSend = 1_000 ether; + + uint256 feePayment = (_fee * _toSend) / _decimals; + + _transferMockERC20(address(kyotoPayWrapper), _toSend); + + kyotoPayWrapper.sendRecipientFunds(address(mockERC20), RANDOM_USER, _toSend); + + assertEq(mockERC20.balanceOf(address(kyotoPayWrapper)), feePayment); + assertEq(mockERC20.balanceOf(RANDOM_USER), (_toSend - feePayment)); + } +} + +contract Withdraw is Test, Helper { + using SafeERC20 for ERC20; + + KyotoPay kyotoPay; + ERC20 mockERC20; + + uint256 _toTransfer = 10 ether; + + function setUp() public { + kyotoPay = new KyotoPay(FEE, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + mockERC20 = ERC20(new MockERC20()); + } + + function transferMock() internal { + mockERC20.safeTransfer(address(kyotoPay), _toTransfer); + assertEq(mockERC20.balanceOf(address(kyotoPay)), _toTransfer); + } + + function test_Withdraw() public { + transferMock(); + + kyotoPay.withdraw(address(mockERC20), _toTransfer); + assertEq(mockERC20.balanceOf(address(kyotoPay)), 0); + } + + function test_Withdraw_RevertIfZeroBalance() public { + vm.expectRevert(IKyotoPay.ZeroBalance.selector); + kyotoPay.withdraw(address(mockERC20), _toTransfer); + } + + function test_Withdraw_RevertIf_NotOwner() public { + vm.prank(RANDOM_USER); + + vm.expectRevert("Ownable: caller is not the owner"); + kyotoPay.withdraw(address(mockERC20), _toTransfer); + } + + function test_Withdraw_RevertIf_NotEnoughBalance() public { + transferMock(); + + uint256 totalBalance = mockERC20.balanceOf(address(kyotoPay)); + + vm.expectRevert("ERC20: transfer amount exceeds balance"); + kyotoPay.withdraw(address(mockERC20), totalBalance + 1); + } +} + +/** + * @dev The Uniswap tests fork mainnet. Forking is much simpler than local deployment of all the Uniswap contracts + */ +contract Pay is Fork { + using SafeERC20 for IERC20; + + KyotoPay kyotoPay; + + function setUp() public override { + // Call Fork setup + Fork.setUp(); + + // mainnetForkId is defined in reference/Fork.sol + mainnetForkId = vm.createSelectFork(MAINNET_RPC_URL, MAINNET_FORK_BLOCK); + kyotoPay = new KyotoPay(FEE, UNISWAP_SWAPROUTER_ADDRESS, WETH_ADDRESS); + + /** + * Add inputs + */ + kyotoPay.addToInputWhitelist(WBTC_ADDRESS); + kyotoPay.addToInputWhitelist(WETH_ADDRESS); + kyotoPay.addToInputWhitelist(DAI_ADDRESS); + kyotoPay.addToInputWhitelist(USDC_ADDRESS); + + /** + * Add outputs + */ + kyotoPay.addToOutputWhitelist(DAI_ADDRESS); + kyotoPay.addToOutputWhitelist(USDC_ADDRESS); + kyotoPay.addToOutputWhitelist(WETH_ADDRESS); + + /** + * Give RANDOM_USER DAI, USDC, ETH, WBTC, and WETH + */ + vm.prank(DAI_HOLDER); + DAI_CONTRACT.safeTransfer(RANDOM_USER, 10_000_000 * (10 ** DAI_DECIMALS)); + + vm.prank(WBTC_HOLDER); + WBTC_CONTRACT.safeTransfer(RANDOM_USER, 10_000 * (10 ** WBTC_DECIMALS)); + + issueUSDC(RANDOM_USER, 10_000_000 * (10 ** USDC_DECIMALS)); + + payable(RANDOM_USER).transfer(20_000 ether); + + vm.startPrank(RANDOM_USER); + IWETH9(WETH_ADDRESS).deposit{value: 10_000 ether}(); + + /** + * Set allowances to type(uint256).max + * msg.sender is RANDOM_USER from startPrank() above + */ + DAI_CONTRACT.safeApprove(address(kyotoPay), type(uint256).max); + USDC_CONTRACT.safeApprove(address(kyotoPay), type(uint256).max); + WETH_CONTRACT.safeApprove(address(kyotoPay), type(uint256).max); + WBTC_CONTRACT.safeApprove(address(kyotoPay), type(uint256).max); + + vm.stopPrank(); + } + + function testFork_SetUp() public { + /** + * Verify inputs + */ + assertTrue(kyotoPay.whitelistedInputTokens(WBTC_ADDRESS)); + assertTrue(kyotoPay.whitelistedInputTokens(WETH_ADDRESS)); + assertTrue(kyotoPay.whitelistedInputTokens(DAI_ADDRESS)); + assertTrue(kyotoPay.whitelistedInputTokens(USDC_ADDRESS)); + + /** + * Verify outputs + */ + assertTrue(kyotoPay.whitelistedOutputTokens(WETH_ADDRESS)); + assertTrue(kyotoPay.whitelistedOutputTokens(DAI_ADDRESS)); + assertTrue(kyotoPay.whitelistedOutputTokens(USDC_ADDRESS)); + + /** + * Verify balances + */ + assertEq(DAI_CONTRACT.balanceOf(RANDOM_USER), 10_000_000 * (10 ** DAI_DECIMALS)); + assertEq(USDC_CONTRACT.balanceOf(RANDOM_USER), 10_000_000 * (10 ** USDC_DECIMALS)); + assertEq(WETH_CONTRACT.balanceOf(RANDOM_USER), 10_000 ether); + assertEq(WBTC_CONTRACT.balanceOf(RANDOM_USER), 10_000 * (10 ** WBTC_DECIMALS)); + assertEq(RANDOM_USER.balance, 10_000 ether); + + /** + * Verify allowances + */ + assertEq(DAI_CONTRACT.allowance(RANDOM_USER, address(kyotoPay)), type(uint256).max); + assertEq(USDC_CONTRACT.allowance(RANDOM_USER, address(kyotoPay)), type(uint256).max); + assertEq(WBTC_CONTRACT.allowance(RANDOM_USER, address(kyotoPay)), type(uint256).max); + assertEq(WETH_CONTRACT.allowance(RANDOM_USER, address(kyotoPay)), type(uint256).max); + + /** + * Verify constants in Helper + */ + assertEq(FEE, kyotoPay.adminFee()); + assertEq(KYOTOPAY_DECIMALS, kyotoPay.DECIMALS()); + } + + function testFork_Pay_RevertIf_RecipientAddressZero() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPay.pay(address(0), USDC_ADDRESS, 100_000_000, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_Pay_RevertIf_WrongUniFee() public { + vm.startPrank(RANDOM_USER); + + uint24 _invalidUniFee = 333; + + vm.expectRevert("Invalid Uni Fee"); + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, 100_000_000, 99_000_000, _invalidUniFee, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_Pay_RevertIfPaused() public { + kyotoPay.pause(); + + vm.expectRevert("Pausable: paused"); + kyotoPay.pay(RANDOM_RECIPIENT, LOOKS_ADDRESS, 100_000_000, 99_000_000, 100, bytes32(0)); + } + + function testFork_Pay_RevertIf_InvalidInputToken() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidToken.selector); + kyotoPay.pay(RANDOM_RECIPIENT, LOOKS_ADDRESS, 100_000_000, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_Pay_RevertIf_InvalidAmountIn() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidAmount.selector); + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, 0, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function tesFork_Pay_RevertIf_InvalidAmountOut() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidAmount.selector); + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, 100_000_000, 0, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_Pay_NotEnoughToken() public { + vm.startPrank(RANDOM_USER); + + uint256 userUSDCBalance = USDC_CONTRACT.balanceOf(RANDOM_USER); + + vm.expectRevert("ERC20: transfer amount exceeds balance"); + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, (userUSDCBalance + 1), 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_Pay_InsufficcientAllowance() public { + vm.startPrank(RANDOM_USER); + + USDC_CONTRACT.safeApprove(address(kyotoPay), 100); + + vm.expectRevert("ERC20: transfer amount exceeds allowance"); + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, 100_000_000, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + /** + * Input: USDC + * Output: WETH + * Note: slippage is set to %0.01%, meaning that nearly any payment should revert + */ + function testFork_Pay_RevertIf_InsufficcientAmountOut() public { + // Random data + bytes32 _data = bytes32(uint256(67)); + + // Amount in is $10,000 of USDC... + uint256 _amountIn = 10_000 * (10 ** USDC_DECIMALS); + + // Set slippage to zero... + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: WETH_ADDRESS, slippageAllowed: uint96(KYOTOPAY_DECIMALS - 1)}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + // Defined in Fork.sol... + (int256 ethUSDCPrice, uint8 ethUSDCDecimals) = getEthToUSDCPriceAndDecimals(); + + // USDC uses 6 decimals + // WETH uses 18 decimals + // Chainlink's pricefeed uses 8 decimals + // However: We need the calculation to end up using the WETH decimals, i.e. 10^18 + + // _amountIn = USDC_Amount * 10^6 + // ethUSDCPrice = ETH_Price * 10^8 + // expectedWeth = (USDC_Amount * 10^6) * (10^8) * (10^(18-6)) / (ETH_Price * 10^8) + // Note: the 10^8s in the nominator and denominator cancel each other out, leaving 10^(18-6) * 10^6 which is just 10^18 + + // Therefore: expectedWeth = (_amountIn) * (10^8) * (10^(18-6)) / ethUSDCPrice + + uint256 expectedWeth = + (_amountIn * (10 ** ethUSDCDecimals) * (10 ** (WETH_DECIMALS - USDC_DECIMALS))) / uint256(ethUSDCPrice); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Too little received"); + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, expectedWeth, 100, _data); + + vm.stopPrank(); + } + + /** + * Input: WETH + * Output: USDC + * Note: slippage is set to 0.01%, meaning that any sufficcient payment will revert + * Surpisingly, slippage ends up being less than 0.01% even at $40,000 payment + * Needed to up the payment amount to $48,000 to have a slippage >0.01% + */ + function testFork_PayEth_RevertIf_InsufficcientAmountOut() public { + // Random data + bytes32 _data = bytes32(uint256(67)); + + // Amount in is ~$48,000 of ether... + uint256 _amountIn = 30 ether; + + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: USDC_ADDRESS, slippageAllowed: uint96(KYOTOPAY_DECIMALS - 1)}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + // Defined in Fork.sol... + (int256 ethUSDCPrice, uint8 ethUSDCDecimals) = getEthToUSDCPriceAndDecimals(); + + // USDC uses 6 decimals + // WETH uses 18 decimals + // Chainlink's pricefeed uses 8 decimals + // However: We need the calculation to end up using the USDC decimals, i.e. 10^6 + + // _amountIn = WETH_Amount * 10^18 + // ethUSDCPrice = ETH_Price * 10^8 + // expectedUSDC = (WETH_Amount * 10^18) * ((ETH_Price * 10^8) * (10**(6-18))) / (10^8) + // Algebraically, 10**(6-18) in the numerator can be made 10**(18-6) in the denominator + // Note: the 10^8s cancel each other out in the numberator and denominator, leaving (10^18)/(10^(18-6)), which is just 10^6 + + // Therefore: expectedUSDC = (_amountIn * ethUSDCPrice) / ((10^8) * (10^(18-6))) + + uint256 expectedUSDC = + (_amountIn * uint256(ethUSDCPrice)) / ((10 ** ethUSDCDecimals) * (10 ** (WETH_DECIMALS - USDC_DECIMALS))); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert("Too little received"); + kyotoPay.payEth{value: _amountIn}(RANDOM_RECIPIENT, expectedUSDC, 100, _data); + + vm.stopPrank(); + } + + function testFork_PayETH_RevertIf_Paused() public { + kyotoPay.pause(); + + vm.expectRevert("Pausable: paused"); + kyotoPay.payEth{value: 1 ether}(RANDOM_RECIPIENT, 99_000_000, 100, bytes32(0)); + } + + function testFork_PayETH_RevertIf_RecipientAddressZero() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.ZeroAddress.selector); + kyotoPay.payEth{value: 1 ether}(address(0), 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_PayEth_RevertIf_WrongUniFee() public { + vm.startPrank(RANDOM_USER); + + uint24 _invalidUniFee = 333; + + vm.expectRevert("Invalid Uni Fee"); + kyotoPay.payEth{value: 1 ether}(RANDOM_RECIPIENT, 99_000_000, _invalidUniFee, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_PayEth_RevertIf_InvalidInputToken() public { + kyotoPay.revokeFromInputWhitelist(WETH_ADDRESS); + + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidToken.selector); + kyotoPay.payEth{value: 1 ether}(RANDOM_RECIPIENT, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_PayEth_RevertIf_InvalidAmountIn() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidAmount.selector); + kyotoPay.payEth{value: 0}(RANDOM_RECIPIENT, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_PayEth_RevertIf_InvalidAmountOut() public { + vm.startPrank(RANDOM_USER); + + vm.expectRevert(IKyotoPay.InvalidAmount.selector); + kyotoPay.payEth{value: 1 ether}(RANDOM_RECIPIENT, 0, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_PayEth_RevertIf_NotEnoughETH() public { + vm.startPrank(RANDOM_USER); + + uint256 userETHBalance = RANDOM_USER.balance; + + vm.expectRevert(); + kyotoPay.payEth{value: (userETHBalance + 1)}(RANDOM_RECIPIENT, 99_000_000, 100, bytes32(0)); + + vm.stopPrank(); + } + + function testFork_Pay_NoPreferenceSet() public { + bytes32 _data = bytes32(uint256(67)); + uint256 _amountIn = 100_000_000; + + (address _recipientToken, uint96 _recipientSlippage) = kyotoPay.recipientPreferences(RANDOM_RECIPIENT); + assertEq(_recipientToken, address(0)); + assertEq(_recipientSlippage, uint96(0)); + + (uint256 userUSDCBalanceBefore, uint256 recipientUSDCBalanceBefore, uint256 kyotoUSDCBalanceBefore) = + getTokenBalances(USDC_CONTRACT, RANDOM_USER, RANDOM_RECIPIENT, address(kyotoPay)); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, _data); + + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, 99_000_000, 100, _data); + + vm.stopPrank(); + + (uint256 userUSDCBalanceAfter, uint256 recipientUSDCBalanceAfter, uint256 kyotoUSDCBalanceAfter) = + getTokenBalances(USDC_CONTRACT, RANDOM_USER, RANDOM_RECIPIENT, address(kyotoPay)); + + uint256 adminFee = (_amountIn * FEE) / KYOTOPAY_DECIMALS; + uint256 recipientPayment = _amountIn - ((_amountIn * FEE) / KYOTOPAY_DECIMALS); + + /** + * Assert admin fee and recipientPayment are correct given logic... + */ + assertEq(adminFee, 1_000_000); + assertEq(recipientPayment, 99_000_000); + + assertEq((recipientUSDCBalanceAfter - recipientUSDCBalanceBefore), recipientPayment); + assertEq((userUSDCBalanceBefore - userUSDCBalanceAfter), _amountIn); + assertEq((kyotoUSDCBalanceAfter - kyotoUSDCBalanceBefore), adminFee); + } + + function testFork_Pay_PreferenceSetSameInputAndOutput() public { + bytes32 _data = bytes32(uint256(67)); + uint256 _amountIn = 100_000_000; + + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: USDC_ADDRESS, slippageAllowed: 9_900}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + (address recipientToken, uint96 recipientSlippage) = kyotoPay.recipientPreferences(RANDOM_RECIPIENT); + assertEq(recipientToken, USDC_ADDRESS); + assertEq(recipientSlippage, 9_900); + + (uint256 userUSDCBalanceBefore, uint256 recipientUSDCBalanceBefore, uint256 kyotoUSDCBalanceBefore) = + getTokenBalances(USDC_CONTRACT, RANDOM_USER, RANDOM_RECIPIENT, address(kyotoPay)); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, _data); + + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, 99_000_000, 100, _data); + + vm.stopPrank(); + + (uint256 userUSDCBalanceAfter, uint256 recipientUSDCBalanceAfter, uint256 kyotoUSDCBalanceAfter) = + getTokenBalances(USDC_CONTRACT, RANDOM_USER, RANDOM_RECIPIENT, address(kyotoPay)); + + uint256 adminFee = (_amountIn * FEE) / KYOTOPAY_DECIMALS; + uint256 recipientPayment = _amountIn - ((_amountIn * FEE) / KYOTOPAY_DECIMALS); + + /** + * Assert admin fee and recipientPayment are correct given logic... + */ + assertEq(adminFee, 1_000_000); + assertEq(recipientPayment, 99_000_000); + + assertEq((recipientUSDCBalanceAfter - recipientUSDCBalanceBefore), recipientPayment); + assertEq((userUSDCBalanceBefore - userUSDCBalanceAfter), _amountIn); + assertEq((kyotoUSDCBalanceAfter - kyotoUSDCBalanceBefore), adminFee); + } + + function testFork_Pay_PreferenceInputUsdcAndOutputWeth() public { + // Random data + bytes32 _data = bytes32(uint256(67)); + + // Amount in is $10,000 of USDC... + uint256 _amountIn = 10_000 * (10 ** USDC_DECIMALS); + + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: WETH_ADDRESS, slippageAllowed: 9_900}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + /** + * Store before balances... + */ + (uint256 recipientWethBalanceBefore, uint256 kyotoWethBalanceBefore,) = + getTokenBalances(WETH_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 userUSDCBalanceBefore = USDC_CONTRACT.balanceOf(RANDOM_USER); + + // Defined in Fork.sol... + (int256 ethUSDCPrice, uint8 ethUSDCDecimals) = getEthToUSDCPriceAndDecimals(); + + // USDC uses 6 decimals + // WETH uses 18 decimals + // Chainlink's pricefeed uses 8 decimals + // However: We need the calculation to end up using the WETH decimals, i.e. 10^18 + + // _amountIn = USDC_Amount * 10^6 + // ethUSDCPrice = ETH_Price * 10^8 + // expectedWeth = (USDC_Amount * 10^6) * (10^8) * (10^(18-6)) / (ETH_Price * 10^8) + // Note: the 10^8s in the nominator and denominator cancel each other out, leaving 10^(18-6) * 10^6 which is just 10^18 + + // Therefore: expectedWeth = (_amountIn) * (10^8) * (10^(18-6)) / ethUSDCPrice + + uint256 expectedWeth = + (_amountIn * (10 ** ethUSDCDecimals) * (10 ** (WETH_DECIMALS - USDC_DECIMALS))) / uint256(ethUSDCPrice); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, _data); + + // Correct fee for this pool is 0.05%, which is 500... + kyotoPay.pay(RANDOM_RECIPIENT, USDC_ADDRESS, _amountIn, expectedWeth, 500, _data); + + vm.stopPrank(); + + (uint256 recipientWethBalanceAfter, uint256 kyotoWethBalanceAfter,) = + getTokenBalances(WETH_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 userUSDCBalanceAfter = USDC_CONTRACT.balanceOf(RANDOM_USER); + + uint256 adminFee = (expectedWeth * FEE) / KYOTOPAY_DECIMALS; + uint256 recipientPayment = expectedWeth - adminFee; + + assertEq((userUSDCBalanceBefore - userUSDCBalanceAfter), _amountIn); + + // Approximately equal within 0.25%. + assertApproxEqRel((recipientWethBalanceAfter - recipientWethBalanceBefore), recipientPayment, 0.0025e18); + assertApproxEqRel((kyotoWethBalanceAfter - kyotoWethBalanceBefore), adminFee, 0.0025e18); + } + + function testFork_Pay_PreferenceInputWbtcAndOutputUSDC() public { + // Amount in is ~$22,000 of WBTC + uint256 _amountIn = 1 * (10 ** WBTC_DECIMALS); + + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: USDC_ADDRESS, slippageAllowed: 9_800}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + /** + * Store before balances... + */ + (uint256 recipientUSDCBalanceBefore, uint256 kyotoUSDCBalanceBefore,) = + getTokenBalances(USDC_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 userWbtcBalanceBefore = WBTC_CONTRACT.balanceOf(RANDOM_USER); + + // Defined in Fork.sol... + (int256 btcUSDCPrice, uint8 btcUSDCDecimals) = getBtcToUSDCPriceAndDecimals(); + + // Unlike WETH and ETH, WBTC and BTC don't trade in parity... + (int256 wbtcBtcConversionRate, uint8 wbtcBtcConversionDecimals) = getWbtcToBtcConversionRateAndDecimals(); + + // WBTC uses 8 decimals + // USDC uses 6 decimals + // Chainlink's pricefeeds uses 8 decimals + // However: We need the calculation to end up using the USDC decimals, i.e. 10^6 + + // _amountIn = WBTC_Amount * 10^8 + // btcUSDCPrice = BTC_Price * 10^8 + // wbtcBtcConversionRate = Conversion_Rate * 10^8 + // expectedUSDC = (WBTC_Amount * 10^8) * (Conversion_Rate * 10^8) * (btcUSDPrice * 10^8) * (10^(6-8)) / (10^8) * 10(^8) + // Algebraically, 10^(6-8) in the numerator is the same as 10^(8-6) in the denominator + // Note: the 10^8s in the nominator and denominator cancel each other out, leaving 10^(18-6) * 10^6 which is just 10^18 + + // Therefore: expectedUSDC = _amountIn * wbtcBtcConversionRate * btcUSDCPrice) / (10(8-6) * (10^8) * 10(^8)) + + uint256 expectedUSDC = (_amountIn * uint256(wbtcBtcConversionRate) * uint256(btcUSDCPrice)) + / ((10 ** (WBTC_DECIMALS - USDC_DECIMALS)) * (10 ** btcUSDCDecimals) * (10 ** wbtcBtcConversionDecimals)); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, WBTC_ADDRESS, _amountIn, bytes32(uint256(0))); + + // Correct fee for this pool is 0.3%, which is 3000... + kyotoPay.pay(RANDOM_RECIPIENT, WBTC_ADDRESS, _amountIn, expectedUSDC, 3000, bytes32(uint256(0))); + + vm.stopPrank(); + + (uint256 recipientUSDCBalanceAfter, uint256 kyotoUSDCBalanceAfter,) = + getTokenBalances(USDC_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 userWbtcBalanceAfter = WBTC_CONTRACT.balanceOf(RANDOM_USER); + uint256 adminFee = (expectedUSDC * FEE) / KYOTOPAY_DECIMALS; + + assertEq((userWbtcBalanceBefore - userWbtcBalanceAfter), _amountIn); + + // Approximately equal within 0.50% + // recipientPayment = expectedUSDC - adminFee + assertApproxEqRel((recipientUSDCBalanceAfter - recipientUSDCBalanceBefore), (expectedUSDC - adminFee), 0.005e18); + assertApproxEqRel((kyotoUSDCBalanceAfter - kyotoUSDCBalanceBefore), adminFee, 0.005e18); + } + + function testFork_PayEth_NoPreferencesSet() public { + bytes32 _data = bytes32(uint256(67)); + uint256 _amountIn = 10 ether; + + (address _recipientToken, uint96 _recipientSlippage) = kyotoPay.recipientPreferences(RANDOM_RECIPIENT); + assertEq(_recipientToken, address(0)); + assertEq(_recipientSlippage, uint96(0)); + + uint256 userEthBalanceBefore = RANDOM_USER.balance; + + (uint256 recipientWethBalanceBefore, uint256 kyotoWethBalanceBefore,) = + getTokenBalances(WETH_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, WETH_ADDRESS, _amountIn, _data); + + // Amount out doesn't matter here... + kyotoPay.payEth{value: _amountIn}(RANDOM_RECIPIENT, 99_000_000, 100, _data); + + vm.stopPrank(); + + (uint256 recipientWethBalanceAfter, uint256 kyotoWethBalanceAfter,) = + getTokenBalances(WETH_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 adminFee = (_amountIn * FEE) / KYOTOPAY_DECIMALS; + uint256 recipientPayment = _amountIn - ((_amountIn * FEE) / KYOTOPAY_DECIMALS); + + /** + * Assert admin fee and recipientPayment are correct given logic... + */ + assertEq(adminFee, 0.1 ether); + assertEq(recipientPayment, 9.9 ether); + + assertEq((recipientWethBalanceAfter - recipientWethBalanceBefore), recipientPayment); + assertEq((userEthBalanceBefore - RANDOM_USER.balance), _amountIn); + assertEq((kyotoWethBalanceAfter - kyotoWethBalanceBefore), adminFee); + } + + function testFork_PayEth_EthInputAndWethOutput() public { + bytes32 _data = bytes32(uint256(67)); + uint256 _amountIn = 10 ether; + + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: WETH_ADDRESS, slippageAllowed: 9_900}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + (address recipientToken, uint96 recipientSlippage) = kyotoPay.recipientPreferences(RANDOM_RECIPIENT); + assertEq(recipientToken, WETH_ADDRESS); + assertEq(recipientSlippage, 9_900); + + uint256 userETHBalanceBefore = RANDOM_USER.balance; + + (uint256 recipientWethBalanceBefore, uint256 kyotoWethBalanceBefore,) = + getTokenBalances(WETH_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, WETH_ADDRESS, _amountIn, _data); + + // Amount out doesn't matter here... + kyotoPay.payEth{value: _amountIn}(RANDOM_RECIPIENT, 99_000_000, 100, _data); + + vm.stopPrank(); + + (uint256 recipientWethBalanceAfter, uint256 kyotoWethBalanceAfter,) = + getTokenBalances(WETH_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 adminFee = (_amountIn * FEE) / KYOTOPAY_DECIMALS; + uint256 recipientPayment = _amountIn - ((_amountIn * FEE) / KYOTOPAY_DECIMALS); + + /** + * Assert admin fee and recipientPayment are correct given logic... + */ + assertEq(adminFee, 0.1 ether); + assertEq(recipientPayment, 9.9 ether); + + assertEq((recipientWethBalanceAfter - recipientWethBalanceBefore), recipientPayment); + assertEq((userETHBalanceBefore - RANDOM_USER.balance), _amountIn); + assertEq((kyotoWethBalanceAfter - kyotoWethBalanceBefore), adminFee); + } + + function testFork_PayEth_UsdcOutput() public { + // Random data + bytes32 _data = bytes32(uint256(67)); + + // Amount in is ~$16,000 of ether... + uint256 _amountIn = 10 ether; + + IKyotoPay.Preferences memory _preferences = + IKyotoPay.Preferences({tokenAddress: USDC_ADDRESS, slippageAllowed: 9_900}); + + vm.prank(RANDOM_RECIPIENT); + kyotoPay.setPreferences(_preferences); + + /** + * Store before balances... + */ + (uint256 recipientUSDCBalanceBefore, uint256 kyotoUSDCBalanceBefore,) = + getTokenBalances(USDC_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 userEthBalanceBefore = RANDOM_USER.balance; + + // Defined in Fork.sol... + (int256 ethUSDCPrice, uint8 ethUSDCDecimals) = getEthToUSDCPriceAndDecimals(); + + // USDC uses 6 decimals + // WETH uses 18 decimals + // Chainlink's pricefeed uses 8 decimals + // However: We need the calculation to end up using the USDC decimals, i.e. 10^6 + + // _amountIn = WETH_Amount * 10^18 + // ethUSDCPrice = ETH_Price * 10^8 + // expectedUSDC = (WETH_Amount * 10^18) * ((ETH_Price * 10^8) * (10**(6-18))) / (10^8) + // Algebraically, 10**(6-18) in the numerator can be made 10**(18-6) in the denominator + // Note: the 10^8s cancel each other out in the numberator and denominator, leaving (10^18)/(10^(18-6)), which is just 10^6 + + // Therefore: expectedUSDC = (_amountIn * ethUSDCPrice) / ((10^8) * (10^(18-6))) + + uint256 expectedUSDC = + (_amountIn * uint256(ethUSDCPrice)) / ((10 ** ethUSDCDecimals) * (10 ** (WETH_DECIMALS - USDC_DECIMALS))); + + vm.startPrank(RANDOM_USER); + + vm.expectEmit(true, true, true, true); + emit Payment(RANDOM_RECIPIENT, WETH_ADDRESS, _amountIn, _data); + + // Correct fee for this pool is 0.05%, which is 500... + kyotoPay.payEth{value: _amountIn}(RANDOM_RECIPIENT, expectedUSDC, 500, _data); + + vm.stopPrank(); + + (uint256 recipientUSDCBalanceAfter, uint256 kyotoUSDCBalanceAfter,) = + getTokenBalances(USDC_CONTRACT, RANDOM_RECIPIENT, address(kyotoPay), address(0)); + + uint256 adminFee = (expectedUSDC * FEE) / KYOTOPAY_DECIMALS; + uint256 recipientPayment = expectedUSDC - adminFee; + + assertEq((userEthBalanceBefore - RANDOM_USER.balance), _amountIn); + + // Approximately equal within 0.25%. + assertApproxEqRel((recipientUSDCBalanceAfter - recipientUSDCBalanceBefore), recipientPayment, 0.0025e18); + assertApproxEqRel((kyotoUSDCBalanceAfter - kyotoUSDCBalanceBefore), adminFee, 0.0025e18); + } +} + +// Will get to after demo day.... +// Relatively unimportant, but good to have, especially regarding extreme values in Pay and PayEth + +contract FuzzNoFork is Helper, Test {} + +contract FuzzFork is Fork { +// function testForkFuzz_Pay_SameInputAndOutput() public {} + +// function testForkFuzz_Pay_WbtcInputAndUsdcOutput() public {} +} diff --git a/test/reference/Fork.sol b/test/reference/Fork.sol new file mode 100644 index 0000000..d544555 --- /dev/null +++ b/test/reference/Fork.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import {Helper} from "./Helper.sol"; +import {IUSDC} from "./IUSDC.sol"; +import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract Fork is Helper, Test { + /** + * Mainnet + */ + uint256 mainnetForkId; + string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL"); + uint256 constant MAINNET_FORK_BLOCK = 16520928; + + /** + * Goerli + */ + uint256 goerliForkId; + string GOERLI_RPC_URL = vm.envString("GOERLI_RPC_URL"); + uint256 constant GOERLI_FORK_BLOCK = 8404430; + + /** + * Polygon + */ + uint256 polygonForkId; + string POLYGON_RPC_URL = vm.envString("POLYGON_RPC_URL"); + uint256 constant POLYGON_FORK_BLOCK = 38712600; + + + /** + * Store contracts + */ + IERC20 USDC_CONTRACT = IERC20(USDC_ADDRESS); + IERC20 DAI_CONTRACT = IERC20(DAI_ADDRESS); + IERC20 WBTC_CONTRACT = IERC20(WBTC_ADDRESS); + IERC20 WETH_CONTRACT = IERC20(WETH_ADDRESS); + + /** + * Chainlink Aggregators + */ + AggregatorV3Interface USDC_PER_ETH_PRICE_FEED; + AggregatorV3Interface USDC_PER_BTC_PRICE_FEED; + AggregatorV3Interface WBTC_PER_BTC_PRICE_FEED; + + function setUp() public virtual { + MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL"); + USDC_PER_ETH_PRICE_FEED = AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419); + USDC_PER_BTC_PRICE_FEED = AggregatorV3Interface(0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c); + WBTC_PER_BTC_PRICE_FEED = AggregatorV3Interface(0xfdFD9C85aD200c506Cf9e21F1FD8dd01932FBB23); + } + + function getEthToUSDCPriceAndDecimals() internal view returns (int256, uint8) { + ( + /* uint80 roundID */ + , + int256 price, + /*uint startedAt*/ + , + /*uint timeStamp*/ + , + /*uint80 answeredInRound*/ + ) = USDC_PER_ETH_PRICE_FEED.latestRoundData(); + + require(price > 0, "Negative price value"); + + uint8 decimals = USDC_PER_ETH_PRICE_FEED.decimals(); + + return (price, decimals); + } + + function getBtcToUSDCPriceAndDecimals() internal view returns (int256, uint8) { + ( + /* uint80 roundID */ + , + int256 price, + /*uint startedAt*/ + , + /*uint timeStamp*/ + , + /*uint80 answeredInRound*/ + ) = USDC_PER_BTC_PRICE_FEED.latestRoundData(); + + require(price > 0, "Negative price value"); + + uint8 decimals = USDC_PER_BTC_PRICE_FEED.decimals(); + + return (price, decimals); + } + + function getWbtcToBtcConversionRateAndDecimals() internal view returns (int256, uint8) { + ( + /* uint80 roundID */ + , + int256 price, + /*uint startedAt*/ + , + /*uint timeStamp*/ + , + /*uint80 answeredInRound*/ + ) = WBTC_PER_BTC_PRICE_FEED.latestRoundData(); + + require(price > 0, "Negative price value"); + + uint8 decimals = WBTC_PER_BTC_PRICE_FEED.decimals(); + + return (price, decimals); + } + + /** + * @dev Can only be used when the vm is forked from mainnet + * Issues an amount of USDC given an address + * Requirements: + * - The address cannot be address(0) + * - The address cannot be blacklisted by USDC + */ + function issueUSDC(address _address, uint256 _amount) internal { + // Set msg.sender temporarily to the owner of the USDC contracts + vm.startPrank(USDC_MASTER_MINTER); + + IUSDC(USDC_ADDRESS).configureMinter(USDC_MASTER_MINTER, _amount); + + IUSDC(USDC_ADDRESS).mint(_address, _amount); + + assertGe(IUSDC(USDC_ADDRESS).balanceOf(_address), _amount); + // Set msg.sender to back to normal + vm.stopPrank(); + } + + /** + * @dev Internal function to get token balances from 3 different addresses + */ + function getTokenBalances(IERC20 token, address first, address second, address third) + internal + view + returns (uint256, uint256, uint256) + { + return (token.balanceOf(first), token.balanceOf(second), token.balanceOf(third)); + } +} diff --git a/test/reference/Helper.sol b/test/reference/Helper.sol new file mode 100644 index 0000000..bd0669c --- /dev/null +++ b/test/reference/Helper.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract Helper { + /** + * + * Constants + * + */ + uint256 constant FEE = 100; + uint256 constant KYOTOPAY_DECIMALS = 10_000; + // Uniswap's swap router + address constant UNISWAP_SWAPROUTER_ADDRESS = 0xE592427A0AEce92De3Edee1F18E0157C05861564; + address constant RANDOM_USER = 0x5782F98E183f4F2BfBC7deD68141Fe6914307B3F; + address constant RANDOM_RECIPIENT = 0x8313b3727E47efaaBB90b7C2f00A73758D52A2b5; + address constant USDC_ADDRESS = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address constant WBTC_ADDRESS = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + address constant LOOKS_ADDRESS = 0xf4d2888d29D722226FafA5d9B24F9164c092421E; + address ADMIN = address(this); + + // EOA that has ~$39 million in DAI + // Reference: https://etherscan.io/address/0xb527a981e1d415af696936b3174f2d7ac8d11369 + address constant DAI_HOLDER = 0xb527a981e1d415AF696936B3174f2d7aC8D11369; + + // EOA that has ~$273 million in WBTC + // Reference: https://etherscan.io/address/0x218b95be3ed99141b0144dba6ce88807c4ad7c09 + address constant WBTC_HOLDER = 0x218B95BE3ed99141b0144Dba6cE88807c4AD7C09; + + // Reference: https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#readProxyContract#F14 + address constant USDC_MASTER_MINTER = 0xE982615d461DD5cD06575BbeA87624fda4e3de17; + + // Reference: https://etherscan.io/token/0x6b175474e89094c44da98b954eedeac495271d0f#readContract#F5 + uint256 constant DAI_DECIMALS = 18; + + // Reference: https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#readProxyContract#F11 + uint256 constant USDC_DECIMALS = 6; + + // Reference: https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#readContract#F3 + uint256 constant WETH_DECIMALS = 18; + + // Reference: https://etherscan.io/token/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599?a=mint#readContract#F4 + uint256 constant WBTC_DECIMALS = 8; + + /** + * + * Events + * + */ + event Payment(address recipient, address indexed tokenAddress, uint256 indexed amountIn, bytes32 indexed data); + + function getAdmin() external view returns (address) { + return address(this); + } +} diff --git a/test/reference/IUSDC.sol b/test/reference/IUSDC.sol new file mode 100644 index 0000000..254aae9 --- /dev/null +++ b/test/reference/IUSDC.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.9.0; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IUSDC is IERC20 { + function isBlacklisted(address) external view returns (bool); + function mint(address, uint256) external; + function configureMinter(address, uint256) external returns (bool); +} diff --git a/test/reference/KyotoPayWrapper.sol b/test/reference/KyotoPayWrapper.sol new file mode 100644 index 0000000..56bf3e1 --- /dev/null +++ b/test/reference/KyotoPayWrapper.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {KyotoPay} from "../../src/KyotoPay.sol"; + +contract KyotoPayWrapper is KyotoPay { + constructor(uint256 _fee, address _uniswapSwapRouterAddress, address _wethAddress) + KyotoPay(_fee, _uniswapSwapRouterAddress, _wethAddress) + {} + + function validatePreferences(Preferences memory _preferences) external view returns (bool) { + return _validatePreferences(_preferences); + } + + function getSenderFunds(address _tokenAddress, uint256 _amountIn) external { + _getSenderFunds(_tokenAddress, _amountIn); + } + + function sendRecipientFunds(address _tokenAddress, address _recipient, uint256 _amount) external { + _sendRecipientFunds(_tokenAddress, _recipient, _amount); + } +} diff --git a/test/reference/MockERC20.sol b/test/reference/MockERC20.sol new file mode 100644 index 0000000..0082964 --- /dev/null +++ b/test/reference/MockERC20.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MockERC20 is ERC20 { + uint256 private constant TOTAL_SUPPLY = 100_000_000 ether; + + constructor() ERC20("Mock", "MOCK") { + _mint(msg.sender, TOTAL_SUPPLY); + } +}